Perl 6 small stuff #16: All your base are belong to us

It’s the second week of the Perl Weekly Challenge, and like last week we’ve got two assignments — one “beginner” and one “advanced”.

The advanced assigment this time was: “Write a script that can convert integers to and from a base35 representation, using the characters 0–9 and A-Y.” Even though this is a blog mainly about Perl 6 I thought it’d be fun to start with my Perl 5 solutions to the advanced assigment, just so it’s even more easy to appreciate the simplicity of the Perl 6 solution… although not, as you will see, without some discussion.

PERL 5
# Convert from base35 to base10
perl -E '%d = map { $_ => $c++ } (0..9,A..Y); $i = 1; for (reverse(split("", @ARGV[0]))) { $e += $i * $d{$_}; $i = $i * 35; } say $e' 1M5
# Output: 2000
# Convert from base10 to base35
perl -E '%d = map { $c++ => $_ } (0..9,A..Y); while ($ARGV[0] > 0) { push @n, $d{$ARGV[0] % 35}; $ARGV[0] = int($ARGV[0] / 35); } say join("", reverse(@n));' 2000
# Output: 1M5

So these are working one-liners but hardly readable ones. They also violate a lot of best practices. So I expanded them into a full script that’s easier to reuse and understand with added strict and error handling as well as support for positive/negative (+and - prefixes).

#!/usr/bin/env perl
#
# Usage:
# perl base35.pl [+-]NUMBER FROM-BASE, e.g.
#
# perl base35.pl 1000 10
# Output: SK
#
# perl base35.pl SK 35
# Output: 1000
#
# perl base35.pl -SK 35
# Output: -1000
use v5.18;
say base35_conv(@ARGV);
sub base35_conv {
my ($no, $base) = (uc(shift), shift);
if ($base != 10 && $base != 35) {
warn "Not a valid base, must be 10 or 35";
return -1;
}
if (($base == 35 && $no !~ /^[\+\-]{0,1}[0-9A-Y]+$/) || ($base == 10 && $no !~ /^[\+\-]{0,1}[0-9]+$/)) {
warn "You have to provide a valid number for the given base";
return -1;
}
my ($c, $e) = (0, 0);
my $prefix = $no =~ s/^(\+|-)// ? $1 : "";
my %d = map { if ($base == 35) { $_ => $c++ } else { $c++ => $_ } } (0..9,'A'..'Y');
if ($base == 35) {
my $i = 1;
for (reverse(split("", $no))) {
$e += $i * $d{$_};
$i = $i * 35;
}
}
else {
my @digits;
while ($no > 0) {
push @digits, $d{$no % 35};
$no = int($no / 35);
}
$e = join("", reverse(@digits));
}
return ( $prefix ? $prefix : "" ) . $e;
}

There’s really not much to comment about the code above. It works and is reasonably readable. It’s quite long, however, and that’s where Perl 6 comes in and destroys it.

PERL 6
# Convert from base35 to base10
perl6 -e 'say "1M5".parse-base(35)'
# Output: 2000
# Convert from base10 to base35
perl6 -e 'say 2000.base(35)'
# Output: 1M5

At this you’re allowed to stop for a second an appreciate the simplicity of Perl 6. But:

Since these are built-in functions in Perl 6 this wasn’t — in my opinion — the best Perl 6 assigment. I guess the point of the assigment is to write a solution from scratch —had I solved the Perl 5 version of the assigment by using a ready-made CPAN module such as Math::Int2Base I’d feel that I cheated. Maybe that’s just me?


As for the “beginner” assigment this time — “Write a script or one-liner to remove leading zeros from positive numbers” — my Perl 6 and Perl 5 solutions are identical:

perl -E 'say "001000"*1;'
perl6 -e 'say "001000"*1;'
# Both output: 1000

Although the assignment wants a script that removes leading zeroes from positive numbers, this will just as easily remove them from negative numbers as well. These will also work on floating point numbers:

perl -E 'say "-001000"*1;'
perl6 -e 'say "-001000"*1;'
# Both output: -1000
perl -E 'say "001000.234"*1;'
perl6 -e 'say "001000.234"*1;'
# Both output: 1000.234

You can take it one step further with Perl 6, though. Should you for some reason — and I’m not able to think of a good one to be honest — want to do the same on a fraction, this is the way to do it:

perl6 -e 'say "003/4".Rat.nude.join("/");'
# Output: 3/4

.nude returns a two-element list with the numerator and denominator so that we can choose how to represent it (a naive say "3/4"*1; would print 0.75 and would therefore not be a satisfying solution considering how the assignment is specified).


So that’s it for now. It may sound a little silly to write this in a Perl 6 centric blog, but what made the assignment interesting this week was Perl 5.

I look forward to next week’s assignment already.