lzy
ふたたびMatzにっきより
http://www.rubyist.net/~matz/20070807.html#p01
_ [Ruby] Lazy Collectionへの第一歩
になるかどうか自信は無いのだが、 1.9でイテレータ系メソッドにブロックを渡さない時に返される Enumerable::Enumeratorクラスに、外部イテレータとして使うことができるようにnextメソッドを追加した。
実装には最近YARVに追加されたFiberを利用した。っていうか、Fiberって初めて使ったよ。使いやすいような、使いにくいような。慣れてないせいかな。
追加したメソッドは以下の通り
* next - 「次」のオブジェクトを返す。末尾ではStopIteration例外
* next? - 「次」のオブジェクトがあればtrue
* rewind - nextでのオブジェクト生成を最初に「巻き戻す」
JavaのIteratorと同じインターフェイスですね。JavaのIteratorはnextで次のオブジェクトを取得し、hasNextで次があるか調べます。
これには欠点があります。「次のオブジェクトがあるかの判定を、反復前に知るのは難しい場合がある」です。
例えば、↓
まめめも より
http://d.hatena.ne.jp/ku-ma-me/20070817
常に 1 つ分だけ先読みするようです。考えてみれば当然 *1 ですが、ちょっと落とし穴かも。
このように、先読みが必要になったりします。
Xtalでは下のは 1 2 3 4 5 と順番に出力されます。*1
g: fiber{ yield 1; 2.p; yield 3; 4.p; yield 5; 6.p; } it, value: g.iter_next; value.p; it, value = g.iter_next; value.p; it, value = g.iter_next; value.p;
C#の似たようなものとして、IEnumeratorというのがありますが、それの方式は、
- MoveNextで次の要素に移動
- 移動できたら真、移動できなかったら偽を返す
- Currentで現在の要素を取得する
というインターフェイスです。
これだと上のような問題は起きません。*2
欠点としては、要素をCurrentメソッドで取得できるよう、現在の要素を保持していないといけない、ということでしょうか。
そして、Javaの方式でもC#の方式でも同様に当てはまる欠点として、「一回の反復につき、2回以上のメソッド呼び出しが必要となってしまう」という問題があります。*3
そこでXtalでは、多値で [次の要素に移動できたか?, 次の要素*4]を一つのメソッド iter_next *5 が返すようにしています。*6
これで
- 先読みしなくて良くなる
- メソッド呼び出し回数の削減
- Iteratorが現在の要素を保持する必要なし
とよいこと尽くめになるような気がします。