Bug #7493
closedブロックを渡された場合最初の1要素のみを返すEnumeratorに対してnextを送り続けると、2度目にStopIteration例外が発生する
Description
「ブロック付きで動かした場合、最初に見つかった1要素のみを返すメソッド」からEnumeratorを作ると、
2度目のnextでStopIteration例外を返すようです。
思い至った限りで確認したメソッドは、以下の物です。
- Array#index
- Array#find_index
- Array#rindex
- Enumerable#detect
- Enumerable#find
これは、1.8.7と1.9間の仕様に於ける差異と考えた方が良いのでしょうか?
生成されたEnumeratorに対してto_aを送ると全要素を含むArrayが返る事から、
1.8.7に於いても全要素走査出来る方が自然では無いかと感じました。
to_aとの差異は、dbussinkの指摘で気づくことが出来ました。¶
https://github.com/rubinius/rubinius/pull/2063#issuecomment-10881875¶
array = [:a, :b, :c]
index_enum = array.index
index_enum.to_a #=> [:a, :b, :c]
index_enum.next #=> :a
index_enum.next #=> StopIteration: iteration reached at end
Files
Updated by knu (Akinori MUSHA) about 12 years ago
1.8では、nextは lib/generator.rb で実装されています。
ブロック(の返り値)を欲するメソッドのEnumeratorをnextで回すというのは、つまり必要なブロックを渡していないわけで、そういう使い方は1.8では想定していませんでした。
つまり、事務的な答えとしては、挙動は不定ですということになります。
ただ、空のブロック({})の評価値がnilになることを考えると、1.9以降のようにnilを返してやるのが自然と言えるでしょうね。
Enumerable#to_a も、内部ではnilを返すブロックで繰り返しています。
直すとしたらこうですが、1.8.7はこの類の修正が入ることはもうないでしょうね。
気になるようであれば、ローカルで当てたりモンキーパッチしてみてください。
一応 ruby_1_8 には入れておきます。
Index: lib/generator.rb¶
--- lib/generator.rb (revision 38134)
+++ lib/generator.rb (working copy)
@@ -69,7 +69,7 @@ class Generator
def initialize(enum = nil, &block)
if enum
@block = proc { |g|
- enum.each { |x| g.yield x }
- enum.each { |x| g.yield x; nil }
}
else
@block = block
Updated by knu (Akinori MUSHA) about 12 years ago
- Status changed from Open to Closed
- % Done changed from 0 to 100
This issue was solved with changeset r38145.
Kenichi, thank you for reporting this issue.
Your contribution to Ruby is greatly appreciated.
May Ruby be with you.
Make an internal block of Generator return nil instead of self.
- lib/generator.rb (Generator#initialize): Make an internal block
return nil instead of self. [Bug #7493]
Updated by knu (Akinori MUSHA) about 12 years ago
あと、nextが割り込む位置も1.8と1.9以降では違っています。
破壊的なメソッドを例に取ると分かりやすいですが、
a=[1,2,3]
e=a.map!
p e.next #=> 1
p e.next #=> 2
p a #=> [nil, nil, 3] (1.8) [nil, 2, 3] (1.9+)
と、1.8ではnextで値が返ってくるのはyieldによるブロック呼出が終了した後(というか次のブロック呼出があった時)ですが、
1.9以降では元のメソッドがyieldした直後のタイミングで値が返って来、次のnextで元のメソッドに制御が戻るようになっています。
(1.9以降の挙動が望ましいと思います)
Updated by kachick (Kenichi Kamiya) about 12 years ago
- 頂いたパッチを反映させた後、期待通りの動作になることを確認しました。
- 1.8.7で本パッチの取り込まれる可能性が低いという点について理解出来ました。
また、1.8と1.9+でのnext割込箇所の差異はこれまで意識したことがありませんでした。
空ブロックの評価値から設計の方向性を判断する点等、大変勉強になります。
御対応・御教示の程、有難うございました。