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でのオブジェクト生成を最初に「巻き戻す」

JavaIteratorと同じインターフェイスですね。JavaIteratorは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が現在の要素を保持する必要なし

とよいこと尽くめになるような気がします。

*1:6は出力されません

*2:多分!

*3:next?を使わず、nextを使い続け、StopIteraton例外を受け止める方法なら一回で済むのですが、例外のオーバーヘッドが高い実装だと2回のメソッド呼び出しよりコストが高くなる可能性が

*4:次の要素が無ければnull

*5:またはiter_first

*6:Xtalでは、導入をあきらめかけていた「多値」をこれを実現するために取り入れました

gby

http://www.rubyist.net/~matz/20070806.html#p01
Matz日記

Groovyの(Javaにない)演算子について。

「*.」や「.@」についてはどこまでうれしいのかよく分からないけど、レシーバがnilならnilを返すメソッド呼び出し「?.」や、メソッドを取り出す「.&」は、ちょっと欲しいと本気で思った。

Xtalにも既に obj.?member 演算子が定義されています。
意味はちょっと違っていて、「objにmemberが定義されていなかったらnopを返す」という意味です。


Rubyで「レシーバがnilならnilを返すメソッド呼び出し」だと、nilが持つメソッドの呼び出しを .? で出来なくなるような気がします。 nil.?to_s はnilになっちゃいますよね。
それで問題があるのかというと、えーと、特にないのかな。to_sはObjectに定義されてるから、to_sの時には .? を使わなければいいだけで。