2009年10月25日日曜日

配列操作のちょっと不思議な挙動~配列は参照型?~

配列操作で直感的に違和感を感じた。


> a = [1,2,3]
> b = a
> a[0] = 0
=> b = [0,2,3]


bにaを代入して、aの中身を書き変えたらbの値まで書き変わってしまうらしい。
感覚的にはbはコピーしたもので全く別ものだから内容は書き変わってほしくないものだけど。

ちょっとググってみたらpythonで似た様な現象について紹介されてたから、rubyでもちょっと試してみた。
参考: http://weblog.imaginaryworld.org/2008/05/blog-post_08.html


> a = [1,2,3]
> b = Array.new a
> a[0] = 0
=> b = [1,2,3]


うん。できた。
新しいオブジェクトを作ってあげてそれを代入すれば問題なし。

さて、じゃ、ここでこちらにもあるディープコピー問題はどうするんだろ?って思って試してみた。


> a = [1,2,3]
> b = [a, 1]
> c = Array.new b
=> c = [[1,2,3], 1]
> a[0] = 0
=> b = [[0,2,3], 1]
=> c = [[0,2,3], 1]


うん。
cは新しいオブジェクトを作って代入したにも関わらずaの変更が反映されている。
bのオブジェクトのコピーがaを参照しているからダメらしい。

対処として、cloneやdepなんかの配列のコピーが大丈夫か?と思ってやってみたけど、駄目だった。cloneやdupは浅いコピーであり、オブジェクト自身を複製するだけで、オブジェクトの指している先を複製するわけではないとのこと。そりゃ新しく配列オブジェクトを作成したさっきと同様の結果になる。

どうすればいいんだろ?ってことでさらにググってみたら以下の方法でディープコピーができるらしい。


> a = [1,2,3]
> b = [a, 1]
> c = Marshal.load(Marshal.dump(b))
=> c = [[1,2,3], 1]
> a[0] = 0
=> b = [[0,2,3], 1]
=> c = [[1,2,3], 1]


うん、できた。
ファイルに書き出してそれをまた配列に戻す。

なんという力技。

0 件のコメント: