So, onward to addition. Here’s Rakudo’s code for adding two Rats:
multi sub infix:<+>(Rat $a, Rat $b) { my $gcd = pir::gcd__iii($a.denominator, $b.denominator); ($a.numerator * ($b.denominator div $gcd) + $b.numerator * ($a.denominator div $gcd)) / (($a.denominator div $gcd) * $b.denominator); }
And here’s my first stab at a translation to Math::FatRat (named FR+
because of the issues discussed in my last post):
multi sub infix:<FR+>(Math::FatRat $a, Math::FatRat $b) is export(:DEFAULT) { my $gcd = gcd($a.denominator, $b.denominator); Math::FatRat.new($a.numerator * ($b.denominator div $gcd) + $b.numerator * ($a.denominator div $gcd), ($a.denominator div $gcd) * $b.denominator); }
The big difference here is calling Math::FatRat.new
instead of infix:</>
for object construction. You might think we could define an infix:</>
that took two Math::BigInt
objects, but that would move our code away from our goal of being as close as possible to the Perl 6 spec (because infix:</>
can never turn Ints to a FatRat).
My next step was to think about adding a FatRat and a Rat. Here’s what the code would look like:
multi sub infix:<FR+>(Math::FatRat $a, Rat $b) is export(:DEFAULT) { my $gcd = gcd($a.denominator, $b.denominator); Math::FatRat.new($a.numerator * ($b.denominator div $gcd) + $b.numerator * ($a.denominator div $gcd), ($a.denominator div $gcd) * $b.denominator); }
Notice something about this? Other than the signature, this code is exactly the same. (Warning: at the moment, the gcd function actually cannot handle Ints, but it clearly should.) This got me thinking about ways to avoid duplication.
multi sub infix:<FR+>(Rat | Math::FatRat $a, Rat | Math::FatRat $b) is export(:DEFAULT)
is not actually legal Perl 6. But you could do this
multi sub infix:<FR+>($a where Rat | Math::FatRat, $b where Rat | Math::FatRat) is export(:DEFAULT)
That still seem inelegant. Wonder what the Perl 6 spec says about this?
Well, the spec has a Rational role! It’s not in Rakudo yet, but when it is, you’ll be able to write something like this:
multi sub infix:<FR+>(Rational $a, Rational $b) is export(:DEFAULT) { my $gcd = gcd($a.denominator, $b.denominator); Math::FatRat.new($a.numerator * ($b.denominator div $gcd) + $b.numerator * ($a.denominator div $gcd), ($a.denominator div $gcd) * $b.denominator); }
(I think: Rational is described as a parameterized role, and I admit I’m not quite clear on how to use them.) (And the situation is more complicated than that, because there are envisioned to be all sorts of Rational types, and it’s not clear to me exactly how one can figure out which brand of Rational the result object should be.)
Errr… I was going to launch into a second half on this post, but I just realized it’s already pretty long, so I will talk about gcd and simplifying fractions in my next post.
Leave a Reply