So, in my previous post I started converting a spigot algorithm for calculating pi from Haskell to Perl 6. I apologize for being away for so long, but I’m back at it now.
Interestingly, while I thought the previous stream function was much clearer in p6, this time out I think I have to give the edge to Haskell.
convert :: (Integer,Integer) -> [Integer] -> [Integer]
convert (m,n) xs = stream next safe prod cons init xs
where
init = (0%1, 1%1)
next (u,v) = floor (u*v*n’)
safe (u,v) y = (y == floor ((u+1)*v*n’))
prod (u,v) y = (u - fromInteger y/(v*n’), v*n’)
cons (u,v) x = (fromInteger x + u*m’, v/m’)
(m’,n’) = (fromInteger m, fromInteger n)
The difference comes from Haskell’s extremely elegant on-the-fly pair notation. When I translate that to p6, I get
sub convert($m, $n, @x) {
stream(-> $u { floor($u.key * $u.value * $n); },
-> $u, $y { $y == floor(($u.key + 1) * $u.value * $n); },
-> $u, $y { $u.key - $y / ($u.value * $n) => $u.value * $n; },
-> $u, $x { $x + $u.key * $m => $u.value / $m; },
0/1 => 1/1,
@x);
}
Even with p6′s big advantage in not having to explicitly convert integers to rationals, the pair thing makes this round a win for Haskell, IMO.
Perhaps one of the other p6 programmers out there can think of a more elegant way of handling this…
Update: They sure can! The esteemed TimToady pointed out Perl 6 can do something almost identical to the Haskell approach, skipping Pairs altogether:
sub convert($m, $n, @x) {
stream(-> [$u, $v] { floor($u * $v * $n); },
-> [$u, $v], $y { $y == floor(($u + 1) * $v * $n); },
-> [$u, $v], $y { [$u - $y / ($v * $n), $v * $n]; },
-> [$u, $v], $x { [$x + $u * $m, $v / $m]; },
[0/1, 1/1],
@x);
}
This version compares very well with the Haskell version, IMO!