2017-01-04 8 views
1

私の知る限りでは、次のコードが奇妙に動作する理由を理解しようとしています。私は起こるはずと思った何ハッシュ割り当てのRuby配列が奇妙な動作

[1] pry(main)> a = {} 
=> {} 
[2] pry(main)> a[1] = [[0,0]] * 7 
=> [[0, 0], [0, 0], [0, 0], [0, 0], [0, 0], [0, 0], [0, 0]] 
[3] pry(main)> a[2] = [[0,0]] * 7 
=> [[0, 0], [0, 0], [0, 0], [0, 0], [0, 0], [0, 0], [0, 0]] 
[4] pry(main)> a[1][2][0] = 3 # Should be one value changed, right? 
=> 3 
[5] pry(main)> a 
=> {1=>[[3, 0], [3, 0], [3, 0], [3, 0], [3, 0], [3, 0], [3, 0]], 
2=>[[0, 0], [0, 0], [0, 0], [0, 0], [0, 0], [0, 0], [0, 0]]} 

インデックス2でキー1でハッシュaの配列の一つの値が3に変化するが、その代わりに3に配列全体の変更のすべての最初の値がなければならないということです。ここで何が起こっているのですか、私は何が欠けていますか?ここに私のRubyのバージョンがあります。

$ ruby -v 
ruby 2.3.1p112 (2016-04-26 revision 54768) [x86_64-linux] 

EDIT:

私はまた、次の

[1] pry(main)> a = {} 
=> {} 
[2] pry(main)> a[1] = ([[0,0].dup].dup * 7).dup 
=> [[0, 0], [0, 0], [0, 0], [0, 0], [0, 0], [0, 0], [0, 0]] 
[3] pry(main)> a[2] = ([[0,0].dup].dup * 7).dup 
=> [[0, 0], [0, 0], [0, 0], [0, 0], [0, 0], [0, 0], [0, 0]] 
[4] pry(main)> a[1][2][0] = 3 
=> 3 
[5] pry(main)> a 
=> {1=>[[3, 0], [3, 0], [3, 0], [3, 0], [3, 0], [3, 0], [3, 0]], 
2=>[[0, 0], [0, 0], [0, 0], [0, 0], [0, 0], [0, 0], [0, 0]]} 
[6] pry(main)> a = {} 
=> {} 
[7] pry(main)> a[1] = ([[0,0].clone].clone * 7).clone 
=> [[0, 0], [0, 0], [0, 0], [0, 0], [0, 0], [0, 0], [0, 0]] 
[8] pry(main)> a[2] = ([[0,0].clone].clone * 7).clone 
=> [[0, 0], [0, 0], [0, 0], [0, 0], [0, 0], [0, 0], [0, 0]] 
[9] pry(main)> a 
=> {1=>[[0, 0], [0, 0], [0, 0], [0, 0], [0, 0], [0, 0], [0, 0]], 
2=>[[0, 0], [0, 0], [0, 0], [0, 0], [0, 0], [0, 0], [0, 0]]} 
[10] pry(main)> a[1][2][0] = 3 
=> 3 
[11] pry(main)> a 
=> {1=>[[3, 0], [3, 0], [3, 0], [3, 0], [3, 0], [3, 0], [3, 0]], 
2=>[[0, 0], [0, 0], [0, 0], [0, 0], [0, 0], [0, 0], [0, 0]]} 

を試してみました確かに値をコピーする必要がありますか?

答えて

3

a[1]のすべての要素は同じ配列を表します。

[0,0]は、[[0,0]] * 7を実行するとディープコピーされません。

ソリューション:(!ありがとう@Stefan)a[1] = Array.new(7) { [0,0] }

+1

この問題を解決するには、次のようにします。 'a [1] = Array.new(7){[0,0]}' – Stefan

+0

お互いに感謝しています。 – Coolness

+3

ここでのキーは、イニシャライザ*要素*ではなくイニシャライザ*ブロック*を使用することです。元の問題を見るには: 'a.map(&:object_id)'は、配列内のすべてのオブジェクトが同じ参照であることを示します。 – tadman

0

はここで、将来的にこの問題を診断する方法は次のとおりです。

object_idはあなたのRubyはオブジェクトを保持する "スロット" を教えてくれます。そうでない場合、彼らは別の変数名であるかもしれませんが、彼らはまだ同じオブジェクトを指している、

foo = [] 
bar = [] 
foo.object_id # => 70322472940660 
bar.object_id # => 70322472940640 

:オブジェクトは、固有のIDを持っている必要があります。それは、より高度な用途では望ましいかもしれませんが、一般に、それを変更すると、他のものを変更して混乱させ、歯を噛み砕き、「命名すべきではない」と返す可能性があります。

foo, bar = [[]] * 2 

しかし、:

[[]] * 2 # => [[], []] 

私たちが同じように、バック並行割り当てを使用して割り当てることができる二つの配列を、取得しているようですので、私たちが見たときに例えば、我々はだまさ我々は彼らは私たちが問題を見ることができます保存されている場所を確認するためにfoobarを見て、彼らは両方とも同じスロットを指し、そしてので1を変更すると、他に変更している:

foo.object_id # => 70323794190700 
bar.object_id # => 70323794190700 
foo << 1 
bar # => [1] 

これは、コードを書くときに古くからある問題です。あなたは "値渡し"対 "参照渡し"について学び、言語がどちらか一方をしているときはこの問題が発生します。そして、あなたは問題を避けるために言語を教える方法を学ばなければなりません。