Ruby でプログラミングをしていてハマったので書いておく。
例えば、下のコードで説明する。
hoge という、変数に配列をいれて、それをhoge2に代入する。
hoge2 の先頭から配列要素を取り出して行くとき、同時に hoge の配列要素まで減ってしまう。
コード
hoge = [0,1,2,3,4,5,6,7,8,9,10]
hoge2 = hoge
puts "--- Start ---"
hoge.each do |h|
puts "---------"
puts "hoge2 shift: #{hoge2.shift}"
puts "Array hoge : #{hoge.join(',')}"
puts "Array hoge2: #{hoge2.join(',')}"
end
puts "--- End ---"
実行結果
--- Start ---
---------
hoge2 shift: 0
Array hoge : 1,2,3,4,5,6,7,8,9,10
Array hoge2: 1,2,3,4,5,6,7,8,9,10
---------
hoge2 shift: 1
Array hoge : 2,3,4,5,6,7,8,9,10
Array hoge2: 2,3,4,5,6,7,8,9,10
---------
hoge2 shift: 2
Array hoge : 3,4,5,6,7,8,9,10
Array hoge2: 3,4,5,6,7,8,9,10
---------
hoge2 shift: 3
Array hoge : 4,5,6,7,8,9,10
Array hoge2: 4,5,6,7,8,9,10
---------
hoge2 shift: 4
Array hoge : 5,6,7,8,9,10
Array hoge2: 5,6,7,8,9,10
---------
hoge2 shift: 5
Array hoge : 6,7,8,9,10
Array hoge2: 6,7,8,9,10
--- End ---
なぜ、このようなことが起きるのか。
これは、hoge と hoge2 が同じオブジェクトを参照しているからである。hoge, hoge2 それぞれが違う変数を格納しているわけではないのである。それぞれの変数はメモリ上のあるオブジェクトを参照している。この場合、どちらも同じオブジェクトを参照しているので、hoge2 を shift するとその先のオブジェクトがshiftされ、その結果hogeまでもshiftされた結果となり想定していた動きにならなくなる。
これを回避するにはどうするのか。
Marshalを使用します。これは、Ruby オブジェクトをファイル(または文字列)に書き出したり、読み戻したりする機能を提供するモジュールらしいです。一度、文字列にして、それを load し直す事で新しいオブジェクトを参照することができます。
Marshal.load(Marshal.dump(変数名))
実際に書き換えてみる。
hoge = [0,1,2,3,4,5,6,7,8,9,10]
hoge2 = Marshal.load(Marshal.dump(hoge))
puts "--- Start ---"
hoge.each do |h|
puts "---------"
puts "hoge2 shift: #{hoge2.shift}"
puts "Array hoge : #{hoge.join(',')}"
puts "Array hoge2: #{hoge2.join(',')}"
end
puts "--- End ---"
実行結果
--- Start ---
---------
hoge2 shift: 0
Array hoge : 0,1,2,3,4,5,6,7,8,9,10
Array hoge2: 1,2,3,4,5,6,7,8,9,10
---------
hoge2 shift: 1
Array hoge : 0,1,2,3,4,5,6,7,8,9,10
Array hoge2: 2,3,4,5,6,7,8,9,10
---------
hoge2 shift: 2
Array hoge : 0,1,2,3,4,5,6,7,8,9,10
Array hoge2: 3,4,5,6,7,8,9,10
---------
hoge2 shift: 3
Array hoge : 0,1,2,3,4,5,6,7,8,9,10
Array hoge2: 4,5,6,7,8,9,10
---------
hoge2 shift: 4
Array hoge : 0,1,2,3,4,5,6,7,8,9,10
Array hoge2: 5,6,7,8,9,10
---------
hoge2 shift: 5
Array hoge : 0,1,2,3,4,5,6,7,8,9,10
Array hoge2: 6,7,8,9,10
---------
hoge2 shift: 6
Array hoge : 0,1,2,3,4,5,6,7,8,9,10
Array hoge2: 7,8,9,10
---------
hoge2 shift: 7
Array hoge : 0,1,2,3,4,5,6,7,8,9,10
Array hoge2: 8,9,10
---------
hoge2 shift: 8
Array hoge : 0,1,2,3,4,5,6,7,8,9,10
Array hoge2: 9,10
---------
hoge2 shift: 9
Array hoge : 0,1,2,3,4,5,6,7,8,9,10
Array hoge2: 10
---------
hoge2 shift: 10
Array hoge : 0,1,2,3,4,5,6,7,8,9,10
Array hoge2:
--- End ---
この通り、hoge の値は変更されていません。
これを deep copy って言うらしいです。
その反対は sallow copy