Between masak’s Perl 6 contest and the Perl 6 Advent calendar, I haven’t done a lot of blogging here lately. Apologies!
So, a discussion on #perl6 the other day got me thinking about another possible interesting use of the Real
role. Here’s the basic class:
class TimesPi does Real { has Real $.x; method new($x) { self.bless(*, :$x); } method Bridge() { $.x * pi; } method Str() { $.x ~ "π"; } method perl() { "({ $.x })π"; } } sub postfix:<π>($x) { TimesPi.new($x); }
So, that’s simple enough, right? TimesPi stores a Real
number $.x
internally, and the value it represents is that number times pi. There’s a postfix operator π to make it really easy to construct these numbers. Because we’ve defined a Bridge
method, this class has access to all the normal methods and operators of Real
. Still, as presented above it is pretty useless, but defining some operators hints at a useful purposes for this class.
multi sub infix:<+>(TimesPi $lhs, TimesPi $rhs) { TimesPi.new($lhs.x + $rhs.x); } multi sub infix:<->(TimesPi $lhs, TimesPi $rhs) { TimesPi.new($lhs.x - $rhs.x); } multi sub prefix:<->(TimesPi $n) { TimesPi.new(- $n.x); } multi sub infix:<*>(TimesPi $lhs, Real $rhs) { TimesPi.new($lhs.x * $rhs); } multi sub infix:<*>(Real $lhs, TimesPi $rhs) { TimesPi.new($lhs * $rhs.x); } multi sub infix:<*>(TimesPi $lhs, TimesPi $rhs) { $lhs.Bridge * $rhs.Bridge; } multi sub infix:</>(TimesPi $lhs, Real $rhs) { TimesPi.new($lhs.x / $rhs); } multi sub infix:</>(TimesPi $lhs, TimesPi $rhs) { $lhs.x / $rhs.x; }
With these operators in place, basic arithmetic involving TimesPi numbers will stay in the TimesPi class when appropriate. For instance, if you add two TimesPi numbers, the result will be a TimesPi. The cool thing about this is that it is as exact $.x
values allow, rather than forcing everything to be a floating point calculation of limited accuracy.
We can even take things a step further, using this to perform exact trig calculations:
multi method sin(TimesPi $x: $base = Radians) { return $x.Bridge.sin($base) unless $base == Radians; given $x.x { when Int { 0; } when Rat { given $x.x.denominator { when 1 { 0; } when 2 { ($x.x.numerator - 1) %% 4 ?? 1 !! -1 } # could have a case for 6 as well... $x.Bridge.sin; } } default { $x.Bridge.sin; } } }
This checks for cases where we know the exact value of the result, and returns that if it can, otherwise falling back to the standard Real.sin
method.
Of course, just when I was feeling like I might be on to something here, I realized that $.x
was just the number of degrees in the angle divided by 180. Sigh.
January 19, 2011 at 4:06 pm |
Have you considered parameterizing this on arbitrary irrationals? I can see this being equally useful, for example dealing with √ 2.
January 19, 2011 at 4:11 pm |
The idea had occurred to me, though I didn’t follow the thought very far. I guess it would be pretty easy to do, eh? Hmmm.