Bye Bye Moore

猫マンション建築の野望を胸に零細事業主として資本主義の荒波に漕ぎ出したアラサー男の技術メモ

Enumerator::Lazyを使えばmapメソッド等々で遅延評価ができる。

RubyistといえばEnumerator。
mapやselectでデータをブン回すのは爽快です。
ただ、巨大化するのが目に見えているファイルやデータを素で扱うのは抵抗があります。
メモリが溢れてアバババになるのが自明ですし。
そこで活躍するのが、遅延評価さんです。

map や select などのメソッドの遅延評価版を提供するためのクラス。

動作は通常の Enumerator と同じですが、以下のメソッドが遅延評価を行う (つまり、配列ではなく Enumerator を返す) ように再定義されています。

環境

  • MacBookAir(Mid 2011 ver)
  • ruby 2.0.0p195 (2013-05-14 revision 40734) [x86_64-darwin12.5.0]

使うとEnumerator::Lazyを返す

実際使ってみると

> File.open("hoge.txt") {|f| f.each_line.lazy }
#=> #<Enumerator::Lazy: #<Enumerator: #<File:hoge.txt (closed)>:each_line>>

通常のeach_lineと違い、Enumerator::Lazyを返して来ます。
よって、必要になるまで余計なメモリを喰いません。

実際早い

速度も実際早くなります。

通常

> puts (Date.new(2013)..Date.new(9999)).select {|d| d.day == 13 && d.friday?}.take(10)

遅延評価含み

> puts (Date.new(2013)..Date.new(9999)).lazy.select {|d| d.day == 13 && d.friday?}.take(10).to_a

という、同じ意味合いのスクリプトを走らせると体感で分かる違いがあります。
前者は3秒強、後者は文字通り一瞬です。
=>ベンチマークとりました

編集記録

  • 12/27 スクリプトの順番が逆になっていたので修正 & ベンチマーク記事へのリンク追加