Forum: Ruby-dev [Ruby 1.8 - Bug #7493][Open] ブロックを渡された場合最初の1要素のみを返すEnumeratorに対してnextを送り続けると、2度目にStopIteration例外が発生す

Posted by Kenichi Kamiya (kachick)
on 2012-12-02 05:35
(Received via mailing list)
Issue #7493 has been reported by kachick (Kenichi Kamiya).

----------------------------------------
Bug #7493: 
ブロックを渡された場合最初の1要素のみを返すEnumeratorに対してnextを送り続けると、2度目にStopIteration例外が発生する
https://bugs.ruby-lang.org/issues/7493

Author: kachick (Kenichi Kamiya)
Status: Open
Priority: Normal
Assignee:
Category:
Target version: Ruby 1.8.7
ruby -v: ruby 1.8.7 (2012-10-12 patchlevel 371) [x86_64-linux]


「ブロック付きで動かした場合、最初に見つかった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#iss...

  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
Posted by knu (Akinori MUSHA) (Guest)
on 2012-12-02 14:01
(Received via mailing list)
Issue #7493 has been updated by knu (Akinori MUSHA).


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

----------------------------------------
Bug #7493: 
ブロックを渡された場合最初の1要素のみを返すEnumeratorに対してnextを送り続けると、2度目にStopIteration例外が発生する
https://bugs.ruby-lang.org/issues/7493#change-34315

Author: kachick (Kenichi Kamiya)
Status: Open
Priority: Normal
Assignee:
Category:
Target version: Ruby 1.8.7
ruby -v: ruby 1.8.7 (2012-10-12 patchlevel 371) [x86_64-linux]


「ブロック付きで動かした場合、最初に見つかった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#iss...

  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
Posted by knu (Akinori MUSHA) (Guest)
on 2012-12-02 14:29
(Received via mailing list)
Issue #7493 has been updated by knu (Akinori MUSHA).


あと、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以降の挙動が望ましいと思います)
----------------------------------------
Bug #7493: 
ブロックを渡された場合最初の1要素のみを返すEnumeratorに対してnextを送り続けると、2度目にStopIteration例外が発生する
https://bugs.ruby-lang.org/issues/7493#change-34317

Author: kachick (Kenichi Kamiya)
Status: Closed
Priority: Normal
Assignee:
Category:
Target version: Ruby 1.8.7
ruby -v: ruby 1.8.7 (2012-10-12 patchlevel 371) [x86_64-linux]


「ブロック付きで動かした場合、最初に見つかった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#iss...

  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
Posted by Kenichi Kamiya (kachick)
on 2012-12-02 20:12
(Received via mailing list)
Issue #7493 has been updated by kachick (Kenichi Kamiya).


* 頂いたパッチを反映させた後、期待通りの動作になることを確認しました。
* 1.8.7で本パッチの取り込まれる可能性が低いという点について理解出来ました。

また、1.8と1.9+でのnext割込箇所の差異はこれまで意識したことがありませんでした。
空ブロックの評価値から設計の方向性を判断する点等、大変勉強になります。

御対応・御教示の程、有難うございました。
----------------------------------------
Bug #7493: 
ブロックを渡された場合最初の1要素のみを返すEnumeratorに対してnextを送り続けると、2度目にStopIteration例外が発生する
https://bugs.ruby-lang.org/issues/7493#change-34326

Author: kachick (Kenichi Kamiya)
Status: Closed
Priority: Normal
Assignee:
Category:
Target version: Ruby 1.8.7
ruby -v: ruby 1.8.7 (2012-10-12 patchlevel 371) [x86_64-linux]


「ブロック付きで動かした場合、最初に見つかった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#iss...

  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
Please log in before posting. Registration is free and takes only a minute.
Existing account (Switch to SSL-encrypted connection)
NEW: Do you have a Google/GoogleMail or Yahoo account? No registration required!
Log in with Google account | Log in with Yahoo account
No account? Register here.