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]