From: Yusuke ENDOH Date: 2009-02-05T02:51:16+09:00 Subject: [ruby-dev:37910] [Bug:1.9] lack consistency in hash iteration 遠藤です。 [ruby-core:21812] で調べていて気が付いたことですが、 $ ./ruby -e 'h = {0 => nil}; i = 1; h.each_key { h[i] = nil; i += 1 }' は停止し、 $ ./ruby -e 'h = {0 => nil, 1 => nil}; i = 2; h.each_key { h[i] = nil; i += 1 }' は無限ループになります。 直感的にはどちらも無限ループになると思いました。 特に反対がなければ、以下のパッチをコミットしようと思います。 Index: st.c =================================================================== --- st.c (revision 22051) +++ st.c (working copy) @@ -653,6 +653,7 @@ do { end = ptr->fore == table->head; retval = (*func)(ptr->key, ptr->record, arg); + end = end && (ptr->fore == table->head); switch (retval) { case ST_CHECK: /* check if hash is modified during iteration */ i = ptr->hash % table->num_bins; Index: test/ruby/test_hash.rb =================================================================== --- test/ruby/test_hash.rb (revision 22051) +++ test/ruby/test_hash.rb (working copy) @@ -849,4 +849,30 @@ def test_hash_hash assert_equal({0=>2,11=>1}.hash, {11=>1,0=>2}.hash) end + + def test_iteration_order + h = {1=>nil,3=>nil,2=>nil,4=>nil} + assert_equal([1, 3, 2, 4], h.each_key.to_a) + + h.each_key {|k| h[k + 10] = nil if h.size < 8 } + assert_equal([1, 3, 2, 4, 11, 13, 12, 14], h.each_key.to_a) + + h = {0=>nil} + i = 1 + h.each_key do |k| + break if h.size == 5 + h[i] = nil + i += 1 + end + assert_equal({0=>nil,1=>nil,2=>nil,3=>nil,4=>nil}, h) + + h = {0=>nil,1=>nil} + i = 2 + h.each_key do |k| + break if h.size == 5 + h[i] = nil + i += 1 + end + assert_equal({0=>nil,1=>nil,2=>nil,3=>nil,4=>nil}, h) + end end -- Yusuke ENDOH