ディープコピー 【Ruby】

『オブジェクトのコピー』,『メソッドの引数(値渡し、参照渡し)』の延長です。私もよくわかっていないので、さらっと流します。

配列のコピーや、メソッドの引数に配列を渡すとき、『副作用』(コピー先の値を変更すると、コピー元の値も変更される)が発生するので、『副作用』を避ける場合、cloneメソッドで複製を作る方法を紹介しました。

しかし、多重ループの場合 cloneメソッドでは、2次以上の部分に副作用が残ります。その際に用いるのが、ディープコピー(deep copy)という方法です。

Javaではキチンと?書かないといけないようですが、rubyでは次のコードでディープコピー行えるようです。(*2)

obj_m = Marshal.load(Marshal.dump(obj))

Marshalモジュール : オブジェクトをファイル(または文字列)に書き出したり、読み戻したり する機能を提供するモジュール(*1)
渡したオブジェクトを、Marshal.dump で出力し、Marshal.loadで読み込み、オブジェクトを返すイメージです。

実際にサンプル(sample01)を見てみます、まず clone メソッドでコピーした場合の副作用を見てみます。
4, 5行目 : clone で複製したオブジェクトは別物になっています。
しかし、
6, 7行目 : arrA[0]と、arrB[0]は同じオブジェクトを参照
8, 9行目 : arrA[1]と、arrB[1]は同じオブジェクトを参照
しているので、arrA[0][1]の値を変更した際に、arrBの中の値も変更されています(副作用)。

arrA = Array.new(2){ |i| Array.new(4){ |j| i * 4 + j + 1 } }
p arrA # => [[1, 2, 3, 4], [5, 6, 7, 8]]
arrB = arrA.clone
p arrA.object_id # => 17481360
p arrB.object_id # => 17481200
p arrA[0].object_id # => 17481350
p arrB[0].object_id # => 17481350
p arrA[1].object_id # => 17481340
p arrB[1].object_id # => 17481340
arrA[0][1] = 10
p arrA # => [[1, 10, 3, 4], [5, 6, 7, 8]]
p arrB # => [[1, 10, 3, 4], [5, 6, 7, 8]]

ディープコピーを使ったサンプル(sample02)では、
6, 7, 8, 9行目 : arrA[0], arrB[0], arrA[1], arrB[1] が、それぞれ別々のオブジェクトを参照しています。
arrA[0][1]の値を変更しても、arrBの中の値は変更されていません。

arrA = Array.new(2){ |i| Array.new(4){ |j| i * 4 + j + 1 } }
p arrA # => [[1, 2, 3, 4], [5, 6, 7, 8]]
arrB = Marshal.load(Marshal.dump(arrA))
p arrA.object_id # => 7977880
p arrB.object_id # => 7977670
p arrA[0].object_id # => 7977870
p arrB[0].object_id # => 7977660
p arrA[1].object_id # => 7977860
p arrB[1].object_id # => 7977650
arrA[0][1] = 10
p arrA # => [[1, 10, 3, 4], [5, 6, 7, 8]]
p arrB # => [[1, 2, 3, 4], [5, 6, 7, 8]]

蛇足で、3次元配列もディープコピーを見てみますと、別々のオブジェクトを参照しているようです。

arrA = Array.new(3){ |i| Array.new(2){ |j| Array.new(3){|k| i * 6 + j * 3 + k + 1 }} }
p arrA # => [[[1, 2, 3], [4, 5, 6]], [[7, 8, 9], [10, 11, 12]], [[13, 14, 15], [16, 17, 18]]]
p arrA[0] # => [[1, 2, 3], [4, 5, 6]]
p arrA[0][0] # => [1, 2, 3]
arrB = Marshal.load(Marshal.dump(arrA))
p arrA.object_id # => 17830590
p arrB.object_id # => 17830150
p arrA[0].object_id # => 17830580
p arrB[0].object_id # => 17830140
p arrA[0][0].object_id # => 17830570
p arrB[0][0].object_id # => 17830130

(*1)Ruby 2.1.0 リファレンスマニュアル module Marshal

(*2)Ruby 1.8.7 リファレンスマニュアル instance method Object#clone