🔬60. Examining the Real role of Perl 6, part 3

🔬60. Examining the Real role of Raku, part 3

N. B. Perl 6 has been renamed to Raku. Click to read more.


As promised yesterday, let us take a look at the two methods of the Real role: polymod and base.

polymod

I already devoted a post to the Int.polymod method, but the method also exists in the Real role. Let us see if it is different.

method polymod(Real:D: +@mods) {
    my $more = self;
    my $lazy = @mods.is-lazy;
    fail X::OutOfRange.new(
        :what('invocant to polymod'), :got($more), :range<0..Inf>
    ) if $more < 0; 
    gather { 
        for @mods -> $mod {
            last if $lazy and not $more;
            Failure.new(X::Numeric::DivideByZero.new:
                using => 'polymod', numerator => $more
            ) unless $mod;
            take my $rem = $more % $mod;
            $more -= $rem;
            $more /= $mod;
        }
        take $more if ($lazy and $more) or not $lazy;
    }
}

It looks familiar. Comparing to the method of Int, the separation of lazy and non-lazy lists is incorporated in the main loop. In the rest, it is again the mod operation (in the form of %) and a division (and some additional subtraction).

Try the method on the same 120 (but as a Numeric value):

> say 120.polymod(10,10)
(0 2 1)

> say 120e0.polymod(10,10)
(0 2 1)

The first method is a call of Int.polymod, while the second one is Real.polymod. The results are the same.

A final note on the method. Just notice that it also works with non-integer values:

> 120.34.polymod(3.3, 4.4)
(1.54 0.8 8)

Indeed, 1.54 + 0.8 * 3.3 + 8 * 3.3 * 4.4 = 120.34.

base

The base method converts a number to its representation in a different system, e. g., hexadecimal, octal, or in a system with 5 or 35 digits. Extrapolating hexadecimal system, you may guess that if there are 36 digits, then the digits are 0 to 9 and A to Z.

A few examples with the numbers with a floating point (actually, Rat numbers here):

> 120.34.base(10)
120.34
> 120.34.base(36)
3C.C8N1FU
> 120.34.base(3)
11110.100012
> 120.34.base(5)
440.132223

The fractional part is converted separately. The second argument of the method limits the number of digits in it. Compare:

> 120.34.base(5)
440.132223
> 120.34.base(5, 2)
440.14

I will skip the details of the method internals and will only show the most interesting parts.

The signature of the method in the src/core/Real.pm file is the following:

 method base(Int:D $base, $digits? is copy)

The documentation interprets that quite differently (although correct semantically):

method base(Real:D: Int:D $base where 2..36, $digits? --> Str:D)

The possible digits are listed explicitly (not in ranges):

my @conversion := <0 1 2 3 4 5 6 7 8 9
                   A B C D E F G H I J
                   K L M N O P Q R S T
                   U V W X Y Z>;

Finally, the last gathering of the separate digits into a resulting string is done like that, using a call to the Int.base method:

my Str $r = $int_part.base($base);
$r ~= '.' ~ @conversion[@frac_digits].join if @frac_digits;
# if $int_part is 0, $int_part.base doesn't see the sign of self
$int_part == 0 && self < 0 ?? '-' ~ $r !! $r;

The method also does some heuristics to determine the number of digits after the floating point:

my $prec = $digits // 1e8.log($base.Num).Int;
. . .
for ^$prec {
    last unless $digits // $frac;
    $frac = $frac * $base;
    push @frac_digits, $frac.Int;
    $frac = $frac - $frac.Int;
}

Compare now the method with the same method from the Int class:

multi method base(Int:D: Int:D $base) {
    2 <= $base <= 36
        ?? nqp::p6box_s(nqp::base_I(self,nqp::unbox_i($base)))
        !! Failure.new(X::OutOfRange.new(
            what => "base argument to base", :got($base), :range<2..36>))
}

In this case, all the hard work is delegated to the base_I function of NQP.

And that’s more or less all that I wanted to cover from the Real role internals.

One thought on “🔬60. Examining the Real role of Perl 6, part 3”

Leave a Reply

Your email address will not be published. Required fields are marked *

Retype the CAPTCHA code from the image
Change the CAPTCHA codeSpeak the CAPTCHA code