はじめに
Rubyをつかってコーディングテストに取り組んでいます。
その際に、いろいろなところを参考にして二次元配列を使ったときに、
値の書き換えがうまくいかずに失敗しました。
やりたかったこと
3x5の二次元配列を作成する。初期値はすべて0とする。
[0][0]
の要素の値だけ、1に変更したい。
動作確認環境
- Windows 10
- ruby 2.5.5p157
失敗作
自分が最初に書いた失敗作のコードです。
line[0][0]
だけ1に書き換えたかったのですが、line[1][0]
とline[2][0]
も1に書き換わってしまいました。
line = Array.new(3, Array.new(5, '0'))
line.each do |n|
p n
end
# 実行結果
# ["0", "0", "0", "0", "0"]
# ["0", "0", "0", "0", "0"]
# ["0", "0", "0", "0", "0"]
line[0][0] = '1'
line.each do |n|
p n
end
# 実行結果
# ["1", "0", "0", "0", "0"]
# ["1", "0", "0", "0", "0"]
# ["1", "0", "0", "0", "0"]
なぜ?
各行の配列が同じオブジェクトを参照していました。
下記の実行結果から、配列のオブジェクトIDが3つすべて「21014940」となっています。
そのため、line[0][0]
だけ更新したつもりでも、結果的には同じオブジェクトを更新する結果になりました。
line = Array.new(3, Array.new(5, '0'))
line.each do |n|
p n.object_id
end
# 実行結果
# 21014940
# 21014940
# 21014940
成功例1
最初の二次元配列を作る箇所をArray.new(3).map{Array.new(5, '0')}
とすると、想定通りの結果を得ることができました。
line = Array.new(3).map{Array.new(5, '0')}
line.each do |n|
p n
end
# 実行結果
# ["0", "0", "0", "0", "0"]
# ["0", "0", "0", "0", "0"]
# ["0", "0", "0", "0", "0"]
line[0][0] = '1'
line.each do |n|
p n
end
# 実行結果
# ["1", "0", "0", "0", "0"]
# ["0", "0", "0", "0", "0"]
# ["0", "0", "0", "0", "0"]
もちろん配列のオブジェクトIDがそれぞれ異なるため、line[0][0]
を更新した場合、他の配列に影響を与えることはありません。
line = Array.new(3).map{Array.new(5, '0')}
line.each do |n|
p n.object_id
end
# 実行結果
# 21620400
# 21620240
# 21620080
成功例2
最初の二次元配列を作る箇所をArray.new(3) { Array.new(5,'0') }
としてもよいです。
こちらのほうがコードが短いので、こちらのほうがよいかもしれません。
line = Array.new(3) { Array.new(5,'0') }
line.each do |n|
p n
end
# 実行結果
# ["0", "0", "0", "0", "0"]
# ["0", "0", "0", "0", "0"]
# ["0", "0", "0", "0", "0"]
line[0][0] = '1'
line.each do |n|
p n
end
# 実行結果
# ["1", "0", "0", "0", "0"]
# ["0", "0", "0", "0", "0"]
# ["0", "0", "0", "0", "0"]
おわりに
これのせいで、コーディングテストに失敗しました。
コーディングテストがおわったあと、参照が一緒なんだろうなぁとすぐ気づきました。
制限時間に追われて焦ってしまい、テスト中はさっぱり原因がわかりませんでした。