melborne さんの Object#sequence メソッド(Ruby)

RubyにおけるシーケンスはObject#repeatに任せなさい!

いまはもう更新されていませんが、melborne さんの「hp12c」というブログがあります。この方はおもしろい Rubyist で、僕はときどきヒマに任せてこれを読むのが好きなのですね。文章がうまいし、特殊な才能があるように思われます。

で、この方の考えられた、Ruby の Object#sequence メソッドというのがあるのですよね(参照)。コードはこんなのです。
sequence.rb

class Object
  def sequence(init = true)
    x = self
    Enumerator.new do |y|
      y << x if init
      loop {y << (x = yield x)}
    end
  end
end

わかりますかね。外部イテレータを使った、一種の反復「無限リスト」ジェネレータともいえるでしょうか。もちろんこれは「リスト」(Ruby の配列)だけに留まるものではありません。とにかく「無限リスト」の Ruby での実装として、すばらしいのではないかと思ったのです。実際に Matz もツイッターで反応したくらいですからね。Feature リクエストは結局採用されなかったようですが、それでもおもしろいと思います。


で、どんな風に使うかなのですが、実際に作者がブログ記事でたくさんの例を挙げておられるので、それを御覧下さい。ってそれだけではそっけなさすぎるので、例えば(初項 3、項差 5の)等差数列は

p 3.sequence {|x| x + 5}.take(10)
#=>[3, 8, 13, 18, 23, 28, 33, 38, 43, 48]

という感じです。超簡潔ですね。

フィボナッチ数列

p [1, 1].sequence {|a, b| [b, a + b]}.take(15).map(&:first)
#=>[1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, 377, 610]

すばらしい。

単なる「無限リスト」もこんな感じ。

p 7.sequence(&:succ).take(20)
#=>[7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26]

7 から始まる「無限リスト」ですね。

このメソッドはすごく読みやすくて「きれいな」印象なのですよね。Ruby らしい感じです。


なお、もともとのメソッド名は repeat ですが、作者が Feature リクエストに採用した sequence で統一しました。
何で採用されなかったのですかね。あんまり使われなさそうだからかなあ。

よく考えてみると、Object クラスに入れるのは大袈裟かも知れませんね。Numeric, Array, String くらいで充分な気もする(あ、それだと自作のクラスに使えなくなっちゃうな)。それかせめて Kernel か。採用されなかったのはそのあたりもあるかな。

HaskellScala には iterate という名前でこの機能があるそうです。