Ruby で三角数の無限シーケンスを作る
まずは Enumerator で三角数を具体的に生成してみよう。
def triangles Enumerator.new do |y| sum = 0 1.step {|i| sum += i y << sum } end end p triangles.take(10) #=> [1, 3, 6, 10, 15, 21, 28, 36, 45, 55]
三角数を生成するところをもっと抽象化したい。
iterate を作ってみる。
def iterate(init) x = init Enumerator.new do |y| y << x loop { y << (x = yield x) } end end def triangles2 x = 1 iterate(1) {|sum| x += 1; sum + x } end p triangles2.take(10) #=> [1, 3, 6, 10, 15, 21, 28, 36, 45, 55]
iterate の外側で変数(x)を定義しないようにできないかな。
def triangles3 iterate([1, 2]) {|a, b| [a + b, b + 1] }.lazy.map {|e| e[0] } end p triangles3.take(10).to_a #=> [1, 3, 6, 10, 15, 21, 28, 36, 45, 55]
iterate 内で三角数とインデックスを引き回すようにしてみた。
lazy
の部分はきちんと理解していない。
Haskell には scanl という関数があるみたいだ。
Ruby で scanl を定義してみよう。
module Enumerable def scanl(z) Enumerator.new do |y| y << z x = z each {|e| y << (x += e) } end end end def triangles4 2.step.scanl(1) {|a, b| a + b } end p triangles4.take(10) #=> [1, 3, 6, 10, 15, 21, 28, 36, 45, 55]