Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

alphanumeric counter - howto?

136 views
Skip to first unread message

Oliver Bleckmann

unread,
Nov 11, 2005, 12:33:57 PM11/11/05
to
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?

Brian Wakem

unread,
Nov 11, 2005, 12:52:49 PM11/11/05
to

Purl Gurl

unread,
Nov 11, 2005, 1:01:09 PM11/11/05
to
"Oliver Bleckmann" wrote:

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

What you write is incoherent.

Precisely what do you want to count?
What are your rules for counting?
Included in your displayed range is what?


> Can perl calculate with alphanumeric numbers?

Fetch yourself a scratch pad and an official No. 2 pencil.

Work this problem,

What is the sum of a plus b?

Work towards writing articles which are clear, concise and coherent.

Purl Gurl

Paul Lalli

unread,
Nov 11, 2005, 1:11:02 PM11/11/05
to
Oliver Bleckmann wrote:
> 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...)

Brian Wakem

unread,
Nov 11, 2005, 1:50:43 PM11/11/05
to
Paul Lalli wrote:

> Oliver Bleckmann wrote:
>> 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;


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.

Paul Lalli

unread,
Nov 11, 2005, 2:01:15 PM11/11/05
to
Brian Wakem wrote:

> Paul Lalli wrote:
>
> > 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.
>

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

jl_...@hotmail.com

unread,
Nov 11, 2005, 2:01:18 PM11/11/05
to
> Oliver Bleckmann wrote:
> >
> > I need an alphanumeric counter, which counts from 000000 to ZZZZZZ.
> > Is there an easy way to do this?

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

Brian McCauley

unread,
Nov 11, 2005, 2:03:15 PM11/11/05
to
Oliver Bleckmann wrote:

> 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.

Paul Lalli

unread,
Nov 11, 2005, 2:06:17 PM11/11/05
to
jl_...@hotmail.com wrote:
> 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.

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

Chris Johnson

unread,
Nov 11, 2005, 2:57:00 PM11/11/05
to

Brian McCauley wrote:
> Oliver Bleckmann wrote:
>
> > 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,

sprintf("%b",$_)

> 8,

sprintf("%o",$_)

> 10,

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

> or 16

sprintf("%x",$_)

Brian McCauley

unread,
Nov 11, 2005, 4:20:27 PM11/11/05
to

Brian McCauley wrote:

> 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...".

Jeff

unread,
Nov 12, 2005, 1:17:49 AM11/12/05
to
Paul Lalli wrote:
> 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

Dr.Ruud

unread,
Nov 12, 2005, 8:39:33 AM11/12/05
to
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,

--
Affijn, Ruud

"Gewoon is een tijger."

Dr.Ruud

unread,
Nov 12, 2005, 10:01:34 AM11/12/05
to
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,

Oops:

$ perl -e '$a = "A99"; $a++; print "$a\n"'
B00

(so not AA0)

Oliver Bleckmann

unread,
Nov 12, 2005, 2:36:01 PM11/12/05
to
"Dr.Ruud" <rvtol...@isolution.nl> schrieb im Newsbeitrag
news:dl54n6...@news.isolution.nl...

> $ perl -e '$a = "A99"; $a++; print "$a\n"'
> B00

This ist strange, but I cannot use it for a counter...


Oliver Bleckmann

unread,
Nov 12, 2005, 3:21:58 PM11/12/05
to
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";
}


Bill Segraves

unread,
Nov 12, 2005, 3:37:22 PM11/12/05
to
"Oliver Bleckmann" <Oliver-B...@freenet.de> wrote in message
news:dl2krm$3p6$1...@newsserver.rz.tu-ilmenau.de...

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

Yes. There is an easy way to do this. Hint: Read the documentation for the
functions pop, push, shift, and unshift, as well as the _Perl Cookbook_,
First Edition, Recipe "4.16. Implementing a circular list.

A six-digit Veeder-Root counter, Hobbs Meter, for the pilots among us, or
automotive odometer, is similar to what you've requested, except for the
additional characters.

1. Consider thirty-six characters, Digits 0-9 and the Letters A-Z
(capitals), as the set of characters needed for your counter. Note: Your
problem statement did not make this clear.
2. Now, consider six wheels, each with the thirty-six characters in 1.
above, one wheel for each of the columns in the display of your counter.
3. Starting with the right-most wheel, each time a wheel goes completely
around, it should increment the next wheel on its left by one count, and so
on ...
4. Write a Perl script that "spins" the above-described six wheels and
displays the state of the six-wheel set after each increment of the
right-most wheel.

The above approach, which does not require the use of any of Perl's
arithmetic operators, has no practical limit on the size of the count, as
the count is never represented numerically. My counter is still running, and
has not yet changed the left-most column.

> PS: Can perl calculate with alphanumeric numbers?

See responses from others.

Cheers.
--
Bill Segraves

Dr.Ruud

unread,
Nov 13, 2005, 10:03:40 AM11/13/05
to
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. Sinan Unur

unread,
Nov 13, 2005, 10:25:24 AM11/13/05
to
"Dr.Ruud" <rvtol...@isolution.nl> wrote in news:dl7o6h.1h4.1
@news.isolution.nl:

> 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

--
A. Sinan Unur <1u...@llenroc.ude.invalid>
(reverse each component and remove .invalid for email address)

comp.lang.perl.misc guidelines on the WWW:
http://mail.augustmail.com/~tadmc/clpmisc/clpmisc_guidelines.html

Dr.Ruud

unread,
Nov 13, 2005, 10:46:38 AM11/13/05
to
A. Sinan Unur schreef:
> Dr.Ruud:

>> 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.

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

Brad Baxter

unread,
Nov 15, 2005, 12:02:29 AM11/15/05
to
Dr.Ruud wrote:
> #!/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;
}

--
Brad
ps: If you care, please pardon the use of google groups.
I'm still exploring options since my employer dropped
most newsgroups.

John W. Krahn

unread,
Nov 15, 2005, 1:06:31 AM11/15/05
to

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

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

John
--
use Perl;
program
fulfillment

Dr.Ruud

unread,
Nov 15, 2005, 8:47:57 AM11/15/05
to
John W. Krahn:

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

Nice. But it acts weirdly on bad input:

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

$ perl -MPOSIX -le 'print strtoul "ZZ_________", 36'
12959

John W. Krahn

unread,
Nov 15, 2005, 10:24:37 AM11/15/05
to
Dr.Ruud wrote:
> John W. Krahn:
>
>>$ perl -MPOSIX -le'print strtoul "ZZ", 36'
>>12950
>
> Nice. But it acts weirdly on bad input:
>
> $ perl -MPOSIX -le 'print strtoul "ZZ _", 36'
> 129514
>
> $ perl -MPOSIX -le 'print strtoul "ZZ_________", 36'
> 12959

As the documentation explains, strtol and strtoul return two values.

$ perl -MPOSIX -le 'print for strtoul "ZZ _", 36'
1295
14


Dr.Ruud

unread,
Nov 15, 2005, 10:53:18 AM11/15/05
to
John W. Krahn:
> Dr.Ruud:
>> John W. Krahn:

>>> $ perl -MPOSIX -le'print strtoul "ZZ", 36'
>>> 12950
>>
>> Nice. But it acts weirdly on bad input:
>>
>> $ perl -MPOSIX -le 'print strtoul "ZZ _", 36'
>> 129514
>>
>> $ perl -MPOSIX -le 'print strtoul "ZZ_________", 36'
>> 12959
>
> As the documentation explains, strtol and strtoul return two values.
>
> $ perl -MPOSIX -le 'print for strtoul "ZZ _", 36'
> 1295
> 14

Aargh, I totally missed the order of magnitude from base36('ZZ') to
base10(12950).
For the next time: 36x36 is about 1000, not 10000.


It's a pity though that (yes, as the documentation says) it doesn't work
AIWLI beyond base 36:

$ perl -MPOSIX -le 'print scalar strtoul("ZZ", 62)'
2205

$ perl -MPOSIX -le 'print scalar strtoul("ZZ", 63)'
2240

$ perl -MPOSIX -le 'print scalar strtoul("zz", 62)'
2205

$ perl -MPOSIX -le 'print scalar strtoul("zz", 63)'
2240

Brad Baxter

unread,
Nov 15, 2005, 11:01:02 AM11/15/05
to
Dr.Ruud wrote:
> John W. Krahn:
>
> > $ perl -MPOSIX -le'print strtoul "ZZ", 36'
> > 12950
>
> Nice. But it acts weirdly on bad input:
>
> $ perl -MPOSIX -le 'print strtoul "ZZ _", 36'
> 129514
>
> $ perl -MPOSIX -le 'print strtoul "ZZ_________", 36'
> 12959

Not weird according to the docs. The second value
returned is the number of unparsed characters.

perl -MPOSIX -le'print join "\n", strtoul "ZZ", 36'
1295
0

FWIW, strtol ends at base 36. My interest in this
thread is that for some time I've used base 62 in a
bitvector database index compression/storage
scheme. It started as an experiment, but ended up
working rather nicely. I haven't hit the 2 billion record
mark, so overflow isn't yet an issue I've worried about.

--
Brad

0 new messages