結論だけ知りたい方はコードへジャンプ!
二次元配列を"複製"したのに、コピーを操作するとコピー元も変わってしまう…
AtCoder Beginner Contestにてそんな事象にぶつかってしまい、時間内に解決できませんでした。
たとえば
arr = [[1,2,3],[4,5,6],[7,8,9]]
このような二次元配列を想定します。
これをdupで複製し、項を操作すると…
copy = arr.dup
copy[0][0] = 100
p arr
# => [[100,2,3],[4,5,6],[7,8,9]]
# copyはarrとは別のオブジェクトなのに、copyを操作するとarrも変わってしまった。
複製したのになぜコピー元が影響を受けてしまうんだろう?と頭を捻りました。
なぜコピー元が影響を受けてしまうのか?
copyは確かにarrを複製した別のオブジェクトですが、copy[0]が持っているのはプリミティブな値ではなく、[1,2,3]を格納している参照値(ざっくり言うとメモリアドレスx)を持っています。
つまり、arrでもcopyでも、同一のメモリアドレスxを参照しているのです。
したがってcopyはarrとは別のオブジェクトですが、その中身の配列については、同一の参照先を見るので、同一のオブジェクトです。
arr = [メモリアドレスxの参照値, メモリアドレスyの参照値, メモリアドレスzの参照値]
copy = arr.dup
# copyとarrは間違いなく別のオブジェクトだが…
# copy = [メモリアドレスxの参照値, メモリアドレスyの参照値, メモリアドレスzの参照値]である。
# arrもcopyも同じ参照先を見るので、arr[0]とcopy[0]が指すオブジェクトは同一オブジェクト
# 従って、copy[0][0]を操作すれば、arr[0][0]も影響を受けてしまう。
二次元配列の複製方法
二次元目の配列も複製するためには、一次元目の配列で新しいオブジェクトを用意し、二次元目の配列もdupなどで複製してやる必要があります。
arr = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
copy = []
arr.each do |arr2|
copy.push(arr2.dup)
end
copy[0][0] = 100
p arr
# => [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
# copyの要素を変更したが、arrは影響を受けていない。
以上となります。
もしもっと楽に二次元配列を複製する方法がありましたら、ぜひご教授ください。
ご覧いただきありがとうございました!