RubyのArray#dupで引っかかった
その引っかかった原因から。
2次元配列へのArray#map!
あるテストケースにおいて、予期されるオブジェクトを作るために
とするはずだったのに、結果がおかしい。
ここでそのコードを簡単にしてみたものと、実行結果を示してみる。
UbuntuのRVM上のRuby1.9.2-p180で実行している。
# -*- coding: utf-8 -*- a = [1, 2] b = a.dup b[2] = [3] c = [['1', '2'], ['3', '4']] d = c.dup d[2] = ['5', '6'] print "Array#map実行前\n" print "a: #{a}\n" print "b: #{b}\n" print "a.equal?(b): #{a.equal?(b)}\n" print "c: #{c}\n" print "d: #{d}\n" print "c.equal?(d): #{c.equal?(d)}\n\n" b.map!{|n| n.to_s} d.map!{|ary| ary.map!{|n| n.to_i * 2}} print "Array#map!実行後\n" print "a: #{a}\n" print "b: #{b}\n" print "c: #{c}\n" print "d: #{d}\n"
実行結果
Array#map実行前
a: [1, 2]
b: [1, 2, [3]]
a.equal?(b): false
c: [["1", "2"], ["3", "4"]]
d: [["1", "2"], ["3", "4"], ["5", "6"]]
c.equal?(d): false
Array#map!実行後
a: [1, 2]
b: ["1", "2", "[3]"]
c: [[2, 4], [6, 8]]
d: [[2, 4], [6, 8], [10, 12]]
同様のものをideone.comにも書いてみました。
http://ideone.com/SnuXu
Array#dupの挙動
RubyリファレンスマニュアルのArray#dupの項を引用すると
レシーバと同じ内容を持つ新しい配列を返します。
clone は frozen tainted singleton-class の情報も含めてコピーしますが、 dup は内容だけをコピーします。またどちらのメソッドも要素それ自体のコピーはしません。つまり「浅い(shallow)」コピーを行います。ary = ['string'] p ary #=> ["string"] copy = ary.dup p copy #=> ["string"] ary[0][0...3] = '' p ary #=> ["ing"] p copy #=> ["ing"]
どちらのメソッドも要素それ自体のコピーはしませんとある。
だからリファレンスマニュアルではaryを変化させるとcopyも変化するのか。
自分がちゃんと読まずに内容だけをコピーします、だけに注意してたせいですね。
まとめ
- 2次元配列や、文字列を含んだ配列でArray.dupは気をつけよう
- リファレンスちゃんと読め