lazyについてほんの少し触ってみた

ここ数日自分のTwitterのTLや[ruby-dev]でLazyが入った的な話題がチラホラ出てくるので、snapshotを試してみたら入ってた。おおっと感動したので、実験結果を貼ってみる。
以前、RubyにScalaのパターンガードのようなモノが欲しくなった時にはみたいな実験してたので気にもなってた。

Blog by Railsware | Blog on Engineering, Product Management, Transparency, Culture and many more...も参考に実験。

lazy.rb
p Enumerator.new {|y| a = 0; loop { y << a; a += 1 } }.lazy.select {|a| a > 20 }.take(20)
実行結果
$ ./ruby -v
ruby 2.0.0dev (2012-03-13 trunk 34991) [x86_64-darwin11.3.0]
$ ./ruby lazy.rb 
[21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40]

おなじ実行結果を得るために普通のEnumeratorを使った場合。

p Enumerator.new {|y| a = 0; loop { y << a; a += 1 } }.take(41).select {|a| a > 20 }

あらかじめtake(41)とやっていて作為的。これはどういうリストが返ってくるのか予見せねばならないので、普通ならば逐次配列に追加するなどでもう少し違う書き方をする場面だと思う。

ちなみに下記だと、無限ループになる。

p Enumerator.new {|y| a = 0; loop { y << a; a += 1 } }.select {|a| a > 20 }.take(20)

selectにブロックを付けると配列にしてしまうため、終点が無く永遠に回ってしまう。最初のコードのようにlazyを仲介させるとselectにブロックを付けてもActiveRecordにおけるARelのような中間表現が返されて、Enumratorを回すことが無いみたい。


理解、あってるんだろうか?そもそも普通のEnumeratorを使う機会がすくないので、lazyじゃなくてもできるととかあれば汗。