alphanumeric counter - howto?

O

Oliver Bleckmann

Hi,
I need an alphanumeric counter, which counts from 000000 to ZZZZZZ.
Is there an easy way to do this?

Thanks.

PS: Can perl calculate with alphanumeric numbers?
 
P

Paul Lalli

Oliver said:
Hi,
I need an alphanumeric counter, which counts from 000000 to ZZZZZZ.
Is there an easy way to do this?

I found this module on CPAN:
http://search.cpan.org/src/RHENSSEL/Math-Base36-0.02/Base36.pm

However, it seems to have a very odd bug, in that it strips off the
first 'digit' from the encoded string. If you view the source, and
change decode_base36's last line from:
return(substr($t,1));
to:
return $t;

The module seems to start working correctly (it still fails its fifth
and final test, but I haven't analyzed why yet. After that change, the
following program should do what you want:

#!/usr/bin/perl
use strict;
use warnings;
use Math::Base36 qw/:all/;

for my $i (0..decode_base36('ZZZZZZ')){
print encode_base36($i), "\n";
}

__END__

(After further analysis, I will be attempting to contact the author and
submit a patch for this module...)
 
B

Brian Wakem

Paul said:
I found this module on CPAN:
http://search.cpan.org/src/RHENSSEL/Math-Base36-0.02/Base36.pm

However, it seems to have a very odd bug, in that it strips off the
first 'digit' from the encoded string. If you view the source, and
change decode_base36's last line from:
return(substr($t,1));
to:
return $t;


Quite bizarre. You've got to think he did that for a reason, you can't
accidentally type (substr($t,1)) instead if $t, but as you say, it appears
to be a bug.
 
P

Paul Lalli

Brian said:
Quite bizarre. You've got to think he did that for a reason, you can't
accidentally type (substr($t,1)) instead if $t, but as you say, it appears
to be a bug.

Agreed. I wish the author had included more comments in the code to
explain his/her line of thought. What confuses me more, however is
this: http://testers.cpan.org/show/Math-Base36.html#Math-Base36-0.02
Very few of the testers actually recorded failures. Now, when I
installed the package, although `make test` did show "not ok" for
tests 3-5, this did not hault the installation process (I don't
understand enough about the inner workings of CPAN.pm or the Test
suites to understand why). I wonder if the testers simply saw the end
result that the module installed, without noticing that 3 of the 5
tests failed.

Paul Lalli
 
J

jl_post

Paul Lalli replied:
I found this module on CPAN:
http://search.cpan.org/src/RHENSSEL/Math-Base36-0.02/Base36.pm

However, it seems to have a very odd bug, in that it strips off the
first 'digit' from the encoded string.


Maybe I'm missing something obvious here, but when I look in the
code, I see the line:

my $_digits = '0123456789ABCDEFGHIJKLMNOPQRSTUVWZYX';

Is there any reason why the letters 'X' and 'Z' are swapped? The way
the letters are arranged now would make YYYYYY and XXXXXX bigger than
ZZZZZZ, which is not what the original poster wanted.

-- Jean-Luc
 
B

Brian McCauley

Oliver said:
I need an alphanumeric counter, which counts from 000000 to ZZZZZZ.
Is there an easy way to do this?

There is no built-in way to get a base 36 counter. Perl's builtin
string counter mechanism will count from 000000 to 999999 or from
AAAAAA to ZZZZZZ. (i.e. base 10 or 26).
PS: Can perl calculate with alphanumeric numbers?

You mean numbers in base 36?

Numbers are conceptually just numbers (or if you prefer numbers base
2). It's only when you come to convert them to/from strings that they
can be said have some other base (typically 8,10 or 16).

AFAIK there's no built-in way for perl to convert a number to string
representation in base 2,8,10 or 16.

Perhaps you should look on CPAN for modules with "Base36" in their name.
 
P

Paul Lalli

Paul Lalli replied:


Maybe I'm missing something obvious here, but when I look in the
code, I see the line:

my $_digits = '0123456789ABCDEFGHIJKLMNOPQRSTUVWZYX';

Is there any reason why the letters 'X' and 'Z' are swapped? The way
the letters are arranged now would make YYYYYY and XXXXXX bigger than
ZZZZZZ, which is not what the original poster wanted.

I have no idea why they're swapped either, but this oddness does not
affect the functionality of the module. the $_digits string is used
soley to determine whether or not the characters in the to-be-encoded
string are valid base-36 digits. The ordering is irrelevant. The
actual values of the base-36 digits are computed by subtracting 55 from
the ordinal value of the character in question.

Paul Lalli
 
C

Chris Johnson

Brian said:
There is no built-in way to get a base 36 counter. Perl's builtin
string counter mechanism will count from 000000 to 999999 or from
AAAAAA to ZZZZZZ. (i.e. base 10 or 26).


You mean numbers in base 36?

Numbers are conceptually just numbers (or if you prefer numbers base
2). It's only when you come to convert them to/from strings that they
can be said have some other base (typically 8,10 or 16).

AFAIK there's no built-in way for perl to convert a number to string
representation in base 2,
sprintf("%b",$_)


sprintf("%o",$_)

10,

sprintf("%d",$_) # $_ + 0 would do the same thing.
 
B

Brian McCauley

Brian said:
AFAIK there's no built-in way for perl to convert a number to string
representation in base 2,8,10 or 16.

That should, of course, read "...other than in base...".
 
J

Jeff

Paul said:
Agreed. I wish the author had included more comments in the code to
explain his/her line of thought.

I thought it was mandatory that authors remove any useful comments from code before
publishing to CPAN. I've had my head in a few CPAN modules recently, and I can't find a
useful comment anywhere!

~Jeff
 
D

Dr.Ruud

Brian McCauley:
Perl's builtin
string counter mechanism will count from 000000 to 999999 or from
AAAAAA to ZZZZZZ. (i.e. base 10 or 26).

Or from A00000 to ZZZZZZ?

BTW that also explains the substr(1,
 
O

Oliver Bleckmann

ok, i did it myself.
now this is my solution...

sub base36
{
my ($num) = @_;
use integer;
my @chars = ('0'..'9', 'A'..'Z');
my $result = "";
for(my $b=@chars; $num; $num/=$b)
{
$result .= $chars[$num % $b];
}
return scalar reverse $result;
}

for ($i = 0; $i <= 2000; $i++ )
{
$tmp = base36($i);
for ( $c = length($tmp); $c < 6; $c++ )
{
$tmp = "0" . "$tmp";
}
print "$tmp\n";
}
 
D

Dr.Ruud

Oliver Bleckmann:
sub base36
{
my ($num) = @_;
use integer;
my @chars = ('0'..'9', 'A'..'Z');
my $result = "";
for(my $b=@chars; $num; $num/=$b)
{
$result .= $chars[$num % $b];
}
return scalar reverse $result;
}


Variant:

#!/usr/bin/perl
use strict; use warnings;

use constant Chars => ('0'..'9', 'A'..'Z', 'a'..'z');
use constant MaxBase => scalar Chars;

{ local ($,, $\) = ("\t", "\n");

for (my $i = 0; $i != 30; $i++) {
print $i, int2base($i, 16, 4);
}
}

sub int2base {
use integer;

my ($ret, $num, $base, $minlen) = ('', @_);
return undef if $num < 0
|| ($base ||= 10) < 2
|| MaxBase < $base
|| ($minlen ||= 1) < 1;

for (; $num; $num /= $base) {
$ret .= (Chars)[$num % $base];
}
return scalar reverse $ret . '0'x($minlen - length($ret));
}
 
A

A. Sinan Unur

return undef if $num < 0
|| ($base ||= 10) < 2
|| MaxBase < $base
|| ($minlen ||= 1) < 1;

You should use a plain return here rather than returning undef. Otherwise,
in list context, the sub will return a non-empty list with one element,
foiling conditionals.

Sinan
 
D

Dr.Ruud

A. Sinan Unur schreef:
Dr.Ruud:

You should use a plain return here rather than returning undef.
Otherwise, in list context, the sub will return a non-empty list with
one element, foiling conditionals.

Yes, thanks. I actually had read about it not too long ago...
 
B

Brad Baxter

Dr.Ruud said:
#!/usr/bin/perl
use strict; use warnings;

use constant Chars => ('0'..'9', 'A'..'Z', 'a'..'z');
use constant MaxBase => scalar Chars;

{ local ($,, $\) = ("\t", "\n");

for (my $i = 0; $i != 30; $i++) {
print $i, int2base($i, 16, 4);
}
}

sub int2base {
use integer;

my ($ret, $num, $base, $minlen) = ('', @_);
return undef if $num < 0
|| ($base ||= 10) < 2
|| MaxBase < $base
|| ($minlen ||= 1) < 1;

for (; $num; $num /= $base) {
$ret .= (Chars)[$num % $base];
}
return scalar reverse $ret . '0'x($minlen - length($ret));
}

I assume you left this as an excercise for the reader?

use constant Charstr => join '', Chars;
sub base2int {
use integer;

my ($ret, $num, $base) = (0, @_);
return if $num !~ /^[${\Charstr}]+$/o
|| ($base ||= 10) < 2
|| MaxBase < $base;

for( my $i = length($num)-1, my $c = 0; $i >= 0; --$i ) {
$ret += index(Charstr, substr($num, $i, 1)) * $base**$c++;
}
return $ret;
}
 
J

John W. Krahn

Brad said:
Dr.Ruud said:
#!/usr/bin/perl
use strict; use warnings;

use constant Chars => ('0'..'9', 'A'..'Z', 'a'..'z');
use constant MaxBase => scalar Chars;

{ local ($,, $\) = ("\t", "\n");

for (my $i = 0; $i != 30; $i++) {
print $i, int2base($i, 16, 4);
}
}

sub int2base {
use integer;

my ($ret, $num, $base, $minlen) = ('', @_);
return undef if $num < 0
|| ($base ||= 10) < 2
|| MaxBase < $base
|| ($minlen ||= 1) < 1;

for (; $num; $num /= $base) {
$ret .= (Chars)[$num % $base];
}
return scalar reverse $ret . '0'x($minlen - length($ret));
}

I assume you left this as an excercise for the reader?

use constant Charstr => join '', Chars;
sub base2int {
use integer;

my ($ret, $num, $base) = (0, @_);
return if $num !~ /^[${\Charstr}]+$/o
|| ($base ||= 10) < 2
|| MaxBase < $base;

for( my $i = length($num)-1, my $c = 0; $i >= 0; --$i ) {
$ret += index(Charstr, substr($num, $i, 1)) * $base**$c++;
}
return $ret;
}

And don't forget POSIX::strtol and POSIX::strtoul.

$ perl -MPOSIX -le'print strtoul "ZZ", 36'
12950



John
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,774
Messages
2,569,599
Members
45,169
Latest member
ArturoOlne
Top