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”