はじめに
こんにちは!アメリカの大学で語学を学びながら、独学でソフトウェアエンジニアを目指している者です。
Rubyを使用して勉強を進めている中で、配列の初期化に関して興味深い性質に気づいたので、この記事でその知識を共有したいと思います。
基本的な配列初期化の問題例
行列のように以下のような配列を作成したいと考えてみましょう。
\begin{pmatrix}
0 & 3 & 0 \\
0 & 0 & 0 \\
0 & 0 & 0
\end{pmatrix}
その場合、以下のコードが思い浮かぶのではないでしょうか。
a = Array.new(3, [0, 0, 0])
a[0][1] = 3
p a # => [[0, 3, 0], [0, 3, 0], [0, 3, 0]]
この例では、配列 a
に [0, 0, 0] という初期値の配列を3つ格納しようとしています。
しかし、結果として a
のすべての要素が同じ参照を持つため、1つの要素を変更すると、他のすべての要素にも影響が及びます。
つまり、a[0][1] = 3
の操作が a
のすべての配列に反映されてしまうのです。
なぜこうなるのか
Array.new(3, [0, 0, 0])
では、Rubyが1つのオブジェクト [0, 0, 0]
を作成し、それを配列のすべての要素に割り当てています。
その結果、すべての要素が同じ配列を参照するようになります。このような参照の共有により、1つの要素を変更すると他の要素も影響を受けるという現象が発生するのです。
対処法:個別のオブジェクトを生成する
この問題を解決するために、ブロックを使って要素ごとに異なる配列を生成する方法があります。
a = Array.new(3) { [0, 0, 0] }
a[0][1] = 3
p a # => [[0, 3, 0], [0, 0, 0], [0, 0, 0]]
この方法では、ブロック内で [0, 0, 0]
が3回新しく生成されるため、それぞれの要素が異なるオブジェクトを参照します。
結果として、1つの要素を変更しても他の要素には影響が及ばなくなります。
まとめ
-
Array.new(size, obj)
で配列を初期化すると、すべての要素が同じオブジェクトを参照するため、変更が他の要素にも反映される可能性があります。 - 参照の問題を回避するためには、
Array.new(size) { block }
の形式で個別にオブジェクトを生成するようにしましょう。
これにより、配列内の各要素が独立したオブジェクトとして扱われ、意図しない挙動を防ぐことができます。