2012-07-23
■ [ruby] 円周率を頭から順に計算するやつをRuby 2.0に移植してみた
Ruby 2.0に入る予定のEnumerable#lazyを使って移植してみた。*1
def stream(nxt, safe, prod, cons, seed, strm)
Enumerator.new{|yielder|
loop do
y = nxt[seed]
if safe[seed, y]
seed = prod[seed, y]
yielder << y
else
seed = cons[seed, strm.next]
end
end
}
end
def extr(a, x)
q, r, s, t = *a
return (Rational(q) * x + Rational(r)) / (Rational(s) * x + Rational(t))
end
UNIT = [1, 0, 0, 1]
def comp(a, b)
q, r, s, t = *a
u, v, w, x = *b
return [q*u+r*w, q*v+r*x, s*u+t*w, s*v+t*x]
end
def pi
init = UNIT
lfts = (1..Float::INFINITY).lazy.map{|k| [k, 4*k+2, 0, 2*k+1]}
nxt = ->(z){ extr(z, 3).floor } # Integer -> Integer
safe = ->(z, n){ n == extr(z, 4).floor } # Integer, Integer -> bool
prod = ->(z, n){ comp([10, -10*n, 0, 1], z) }
cons = ->(z, zz){ comp(z, zz)}
return stream(nxt, safe, prod, cons, init, lfts)
end
p pi.take(100)
ほぼHaskell版の素移植。最初、「%」をmodだと勘違いしていてちょっと手間取った。
lfts = (1..Float::INFINITY).lazy.map{|k| [k, 4*k+2, 0, 2*k+1]}
という行がlazyの使用例で、1..∞という無限Rangeをmapして無限列を作っている。
で、streamというメソッドがこの無限列を円周率の各桁を表す無限列([3,1,4,1,5,...])に変換するのだけど、 こっちはなんかうまく書けなくてEnumerator.newを使った。最初はlazy.mapで書けるのかと思ったのだが、 1桁求めるのに元の列を複数個消費するので、mapではだめそうだった。
*1 なお、アルゴリズムの中身については一切理解してない
[ツッコミを入れる]