Python3について調べていたら、辞書(dict型)の順序は不定*1という一文を見つけました。
以前「RubyのHashの順番」について調べていた事を思い出したので、忘れないようにメモ。
生成時の要素順
結論から書くと、1.9系以降はHashの要素の順序(生成順)が保持されるようになりました。*2
Rubyリファレンスマニュアル :class Hash (Ruby 1.8.7)
ハッシュに含まれる要素の順序は保持されません。 列挙する順序は不定です。
Rubyリファレンスマニュアル:class Hash (Ruby 1.9.3)
ハッシュに含まれる要素の順序が保持されるようになりました。 ハッシュにキーが追加された順序で列挙します。
irbで確認。
- 1.8.7
v = {'a' => 1, 'b' => 2, 'c' => 3, 'd' => 4} => {"d"=>4, "c"=>3, "b"=>2, "a"=>1} h = {:b => 2, :d => 4, :a => 1, :c => 3} => {:b=>2, :a=>1, :c=>3, :d=>4}
- 1.9.3
v = {'a' => 1, 'b' => 2, 'c' => 3, 'd' => 4} => {"a"=>1, "b"=>2, "c"=>3, "d"=>4} h = {:b => 2, :d => 4, :a => 1, :c => 3} => {:b=>2, :d=>4, :a=>1, :c=>3}
Hashクラスのメソッドが返す要素の順番
1.9.3以降のHashクラスのメソッドが返す結果について、リファレンスマニュアルには以下のように書いてあります。(リファレンスマニュアル((https://docs.ruby-lang.org/ja/)))
1.9.3
Hash#key => 該当するキーが複数存在する場合、どのキーを返すかは不定。
Hash#shift => どの要素を返すかは不定。
Hash#invert => 異なるキーに対して等しい値が登録されている場合の結果は不定。2.1.0
Hash#key => 該当するキーが複数存在する場合、どのキーを返すかは不定。
Hash#shift => キーが追加された順で先頭の要素をひとつ取り除き、 [key, value]という配列として返します。
Hash#invert => 異なるキーに対して等しい値が登録されている場合、最後に定義されている値が使用されます。
実際にirbで確認。
- 1.9.3
h = {a: 10, b: 10, c: 10, d: 20} h.key(10) #=> :a h.shift #=> [:a, 10] h.invert #=> {10=>:c, 20=>:d}
*2.1.0~
h = {a: 10, b: 10, c: 10, d: 20} h.key(10) #=> :a h.shift #=> [:a, 10] h.invert #=> {10=>:c, 20=>:d}
1.9系も不定ではないようです。
調べてみると、Hash#shift
, Hash#invert
はリファレンスの記述が1.8時代のままだったようで、後のリファレンスでは修正されたみたいです。*3*4
Hash#key
については不定とありますが、挙動としては最初のキーを返しているように見えます。
これについて、過去のチケットを探したら以下のチケットを見つけました。
Bug #4760: Some documentation improvements for Hash#key - Ruby trunk - Ruby Issue Tracking System
一応ruremaにIssueを立てておいたほうがいいのか、また後日考えます。
1/17追記
とりあえずIssueを立てた*5のですが、Hash#keyに関しては#4760のチケットで
Change rdoc from "the first occurence" to "an occurrence" since first occurrence is not a specification of Hash#key.
「最初のキーを返すとは保証されていないのが仕様」と返信されていました。(読み落としていた。。)
答えてくださったsho-hさん、本当にありがとうございました。:)