📘 Spelling numbers using Perl 6

Write an integer number below one million in words.

Human languages have many inconsistencies, especially in the most frequent constructs. Spelling numbers seems to be a simple task, but due to a number of small differences, the resulting program is quite big.

The program is listed on the next page. Let’s discuss the algorithm first.

Take a number; for example, 987,654. The rules for spelling out the groups of three digits, 987and 654, are the same. For the first group, the word thousand must be added.

Now, examine a group of three digits. The first digit is the number of hundreds, and it has to be spelled only if it is not zero. If it is not zero, then we spell the digit and add the word hundred.

Now, remove the leftmost digit, and we’ve got two digits left. If the remaining two digits form the number from 1 to 20, then it can be directly converted to the corresponding name. The names for the numbers from 0 to 10 are obviously different. The names for the numbers from 11 to 19 have some commonalities, but is it still easier to directly prepare the names for all of them.

For the larger numbers (21 to 99), there are two cases. If the number is dividable by 10 then a name for 20, 30, 40, etc. is taken. If not, then the name is built of the name of tens and the name for units, joined with a hyphen, such as forty-five.

The zeroname appears only in the case when the given number is zero.

In the program, the names are listed in the @names array. The ifelsifelse chain implements the above-described procedure.

my @names = <zero one two three four five six seven eight
             nine ten eleven twelve thirteen fourteen fifteen
             sixteen seventeen eighteen nineteen twenty
             thirty forty fifty sixty seventy eighty ninety>;

sub spell-number($number) {
    my $n = $number.Int;

    my $r;
    if $n < 20 {
        $r = @names[$n];
    }
    elsif $n < 100 {        
        $r = @names[$n / 10 + 18];
        $r ~= '-' ~ @names[$n % 10] if $n % 10;
    }
    elsif $n < 1000 {        
        $r = @names[$n / 100] ~ ' hundred';
        $r ~= ' ' ~ spell-number($n % 100) if $n % 100;
    }
    else {
        $r = spell-number($n / 1000) ~ ' thousand';
        $r ~= ' ' ~ spell-number($n % 1000) if $n % 1000;
    }

    return $r;
}

Try running the program with a few different numbers:

say spell-number(987654);  # nine hundred eighty-seven
                           # thousand six hundred fifty-four
say spell-number(0);       # zero
say spell-number(17);      # seventeen
say spell-number(100_001); # one hundred thousand one

All work well, but in Perl 6, you can do it differently with the help of multi-subs. In the next version of the program, the chain of the checks is replaced with the same number of subroutines that know with what numbers they can work best. The common part is extracted to a separate sub spell-part.

my @names = <zero one two three four five six seven eight
             nine ten eleven twelve thirteen fourteen fifteen
             sixteen seventeen eighteen nineteen twenty
             thirty forty fifty sixty seventy eighty ninety>;

multi sub spell-number(Int $n where {$n < 20}) {
    return @names[$n];
}

multi sub spell-number(Int $n where {$n < 100}) {
    my $r = @names[$n / 10 + 18];
    $r ~= '-' ~ @names[$n % 10] if $n % 10;
    return $r;
}

multi sub spell-number(Int $n where {$n < 1000}) {
    return spell-part($n, 100, 'hundred');
}

multi sub spell-number(Int $n where {$n < 1_000_000}) {
    return spell-part($n, 1000, 'thousand');
}

sub spell-part(Int $n, Int $base, Str $name) {
    my $r = spell-number(($n / $base).Int) ~ ' ' ~ $name;
    $r ~= ' ' ~ spell-number($n % $base) if $n % $base;
    return $r;
}

Modify the program to process numbers greater than a million.

📘 Convert to Roman numerals using Perl 6

Convert an integer number to a Roman numerals string.

Roman numbers are not a direct translation of the decimal system. In this task, we assume that the number is not morethan 3999, which is the maximum a regular Roman number can reach.

Let’s use the algorithm that keeps the table of pre-calculated sequences of Roman letters so that we don’t have to check when III becomes IV, or when another I appears after V, etc.

In the program below, there are foursuch sequences: for thousands, hundreds, tens, and ones. The program iterates over the digits of the number in the decimal representation and chooses one of the values from the array of lists stored in the @roman variable.

my $n = 2018;

my $roman;
my @roman = 
    1000 => < M MM MMM >,
    100 => < C CC CCC CD D DC DCC DCCC CM >,
    10  => < X XX XXX XL L LX LXX LXXX XC >,
    1   => < I II III IV V VI VII VIII IX >;

for @roman -> $x {
    my $digit = ($n / $x.key).Int; 
    $roman ~= $x.value[$digit - 1] if $digit;
    $n %= $x.key;
}

say $roman; # MMXVIII

Let us examine the structure of the @roman container. In the program code, it looks like a hash. Indeed, it could be a hash, but in our case, the algorithm requires division by 1000, 100, and 10, so it is easier to organise data so that they are already sorted in the correct order.

The < > quotation construct in the program is the simplest way to create a list from the strings that do not contain spaces. If you print it with the say @roman instruction, you’ll get the following:

[1000 => (M MM MMM) 100 => (C CC CCC CD D DC DCC DCCC CM) 10 => (X XX XXX XL L LX LXX LXXX XC) 1 => (I II III IV V VI VII VIII IX)]

On the top level, it is a list of pairs, such as 1000 => (M MM MMM). Pairs, or objects of the Pair class, have the key and value methods .For the shown example of a pair, the key method returns 1000, and the value method returns a list (M MM MMM).

The digits of the input number $n are scanned from left to right:

my $digit = ($n / $x.key).Int;

The value of $n also becomes smaller and smaller at each step:

$n %= $x.key;

At each iteration, the next digit lands in the $digit variable, and it is used as the index that selects the correct Roman representation:

$roman ~= $x.value[$digit - 1] if $digit; 

As zeros in the decimal form have no correspondence to the Roman numbers, only non-zero values are taken. When the loop is over, the $roman variable contains the desired Roman string.

The opposite conversion is examined in Task 84, Decode Roman numerals.

📘 Compose the largest number using Perl 6

Given the list of integers, compose the largest possible number by concatenating them.

This task requires working with the same data as with numbers and strings. To compose the largest possible number out of a list of integers, we need to reorder them so that the largest numbers come first.

The easiest way to achieve that in Perl 6 is to treat numbers as strings, sort them alphabetically in descending order, concatenate the pieces to a single string, and get the resulting integer.

my @a = (67, 8, 1, 5, 45);

say @a.sort({$^b.Str cmp$^a.Str}).join;

Sorting an array is achieved by the sort method, which optionally takes the code block that is used in comparison:

{$^b.Str cmp $^a.Str}

The two special variables, $^a and $^b, are the so-called placeholders that get the values of the arguments passed to the code block. The order of the arguments corresponds to the alphabetical order of the variables. It can be either $^a and $^b or $^x and $^y or even $^arg1 and $^arg2. As the goal is to sort strings in descending order, $b is mentioned first in comparison. 

During the sorting procedure, $^a and $^b are referencing to different elements of the given array. The cmpoperator is a universal comparison operator in Perl 6. To make sure the arguments are sorted alphabetically, they are both casted to strings by calling the Str method. The result of running the program is 8675451.

📘 Bit counter written in Perl 6

Count the number of bits set to 1 in a binary representation of a positive integer number.

There are two approaches to this task: either treat the binary sequence as a string or count real bits in the machine representation of the number. In Perl, both approaches are fine. We restrict ourselves to the positive integers to avoid dealing with two’s complements representations.

First, use textual approach.

my $value = prompt('Enter value > ');
$value = $value.Int.base(2);
say "Binary representation: $value";
$value ~~ s:g/0//;
say "Number of 1s: {$value.chars}";

The entered value gets converted to a string containing binary representation (see also Task 42, Integer as binary, octal, and hex). Then, all the zeros are removed from the string using the regex substitution:

$value ~~ s:g/0//;

At this point, the $value string contains only set bits, so the length of the string is the number of such bits. To get the length of the string, call the charsmethod (see Task 3, String length).

Run the program:

$ perl6 bit-count.pl
Enter value > 12345
Binary representation: 11000000111001
Number of 1s: 6

Now, the other approach works directly with bits.

my $value = prompt('Enter value > ');

my $bits = 0;
repeat {
   $bits++ if $value +& 1;
   $value = $value +> 1;
} while $value > 0;

say "Number of 1s: $bits";

This program uses two bitwise operators. They are prefixed with ++& for the binary AND operation and +> for the binary shift. 

In the repeat…while loop, the value is tested for the presence of 1in the lowest bit: if $value +& 1. If this bit is set, the $bits counter is incremented.

Then, the $value is shifted to the right by one bit (which is equivalent to dividing it by two). The loop is over when the $value becomes zero, in other words, when all the bits are shifted out.

Notice that the $value variable is seamlessly used first as a string (after returning from the prompt routine), and then as an integer.

Test the program with the same input value as in the first example:

$ perl6 bit-count2.pl
Enter value > 12345
Number of 1s: 6

If there are no performance demands, the first approach using texts seems to be more natural for Perl; however, if you need higher performance, consider pre-calculating the number of bits and saving it in an array.

📘 Computing the sum of digits using Perl 6

Calculate the sum of digits of a given number.

The solution of this task is based on the splitmethod that you can call on any object that is convertible to strings.

The following code takes an integer number and splits it into separate characters, one per digit. Then the reduction operator adds up all the digits, and the result is printed.

my $number = 139487854;
say [+] $number.split('');

The conversion of an integer to a string happens at the moment the splitmethod is called. The resulting array contains a number of one-character elements. When they are passed to the [+] operator, which expects numeric data, characters are converted back to numbers so that they can be added up as numbers, not strings.

Compare the presented solution with a straightforward approach, where the given number is treated as a number, and a series of divisions has to be performed to get separate digits:

my $number = 139487854;
my $sum = 0;

while ($number) {
    $sum += $number % 10;
    $number = Int($number / 10);
}
say $sum; # Still prints 49

📘 Presenting integers as binary, octal, and hex using Perl 6

Print a given integer number in the binary, octal, and hexadecimal representations.

On an integer object, call the basemethod with the corresponding number:

say 42.base(2); # 101010
say 42.base(8);  # 52
say 42.base(16); # 2A

Alternatively, use the fmt method, which is defined for integers and accepts the formatting string in the printfformat:

my $int = 42;
say $int.fmt('Hex: %x'); # Hex: 2a
say $int.fmt('Oct: %o'); # Oct: 52
say $int.fmt('Bin: %b'); # Bin: 101010

In Perl 6, there also exists a conventional function printf, which works similarly to how it behaves in other programming languages: It needs a formatting string and a list of values to be substituted to the %-placeholders.

my $int = 42;
printf("Hex: %0x\n", $int); # Hex: 2a
printf("Oct: %o\n", $int);  # Oct: 52
printf("Bin: %b\n", $int);  # Bin: 101010

The above examples print the values without their radix prefixes. To add a prefix, use the %# forms:

printf("Hex: %#x\n", $int); # Hex: 0x2a
printf("Oct: %#o\n", $int); # Oct: 052 (not 0o52!)
printf("Bin: %#b\n", $int); # Bin: 0b101010

📘 Converting binary to integer using Perl 6

Convert a binary number to a decimal integer.

Perl is good at its ease of switching between numerical and string representation of the same data. This task is an example where this feature can be used.

The idea is to take a binary number, treat it as a string, prepend the 0bprefix indicating the binary value, and convert the value back to the integer, but a decimal this time. This description is encoded in the following example:

my $bin = '101101';
my $int = "0b$bin".Int;
say $int;

This program prints 45.

It is possible to use an alternative representation of the binary number using a generic form of the value with an explicit radix:

my $bin = '101101';
my $int = ":2<$bin>".Int;
say $int;

As a side note, remember that in Perl 6, you cannot start a numeric literal with 0. For example, the following assignment is incorrect:

my $b = 0110;

This generates a compile-time error, saying that the leading 0 no longer means octal numbers. In Perl 6, all the radix prefixes are unified: 0b for binary, 0x for hexadecimal, and 0o for octal representation.