2

次元の正確な構造を維持しようとして、多次元配列のサブセットを返そうとしていますが、何か奇妙なことが起こっています...見てください:

space = [  [ [1],[2],[3] ],  [ [4],[5],[6] ],  [ [70],[8],[9] ]  ]

space_subset = space[(1..2)].collect { |y| y[1] }

=> [[5], [8]] 

それを分解しましょう:

space[(1..2)]

=> [  [ [4], [5], [6] ], [ [70], [8], [9] ]  ]

これで、私が .collect on と呼んでいるものを確信できるようになりました

実際には:

[  [ [4], [5], [6] ], [ [70], [8], [9] ]  ].collect { |y| y[1] }

=> [[5], [8]]

それから...(本当の質問について)...

現在、space_subset が [[5], [8]] の場合

そして、私はそれを次のように変更しようとします:

space_subset[1].delete (8)

期待どおり、次のようになります。=> [[5], []]

サブセット配列を抽出した元の「スペース」配列を同時に変更するのはなぜですか?

今私がするなら:

space

=> [[[1], [2], [3]], [[4], [5], [6]], [[70], [], [9]]]

「8」がありません。space_subset から削除したのと同じ値です

ruby Array api docs を見ていますが、読んでいるものから、コードは驚くことなく動作するはずです...しかし..それでも.....

私が間違っていること、またはここで誤解していることを理解するのを手伝ってもらえますか?

時間を割いて回答してくださった皆様、ありがとうございました

4

2 に答える 2

4

Ruby では、すべてがオブジェクトであるだけでなく、変数は常にオブジェクトへの参照であることを忘れないでください。代わりに取得しているのは元の単一要素配列への参照である場合、ここでコピーが作成されることを期待しています。

これが、多くのオブジェクトにcloneまたはメソッドがある理由です。dup使用する前に何かを変更するつもりであるが、オリジナルを台無しにしたくない場合は、コピーを作成してそれを操作してください。

これを行う簡単な方法は、のようなインプレース修飾子の使用を避けdelete、代わりに次のようなものを使用することですreject:

space_subset[1] = space_subset[1].reject { |v| v == 8 }

これにより、単一の要素が削除され、元の配列からその要素を除いたコピーが返されます。ただし、これは必ずしも最善の方法ではありません。より良いアプローチは、コピーも返すため、不要な要素を単純に「減算」することです。

space_subset[1] -= [ 8 ]

一般に、「所有」していないデータにインプレース修飾子を使用することには注意が必要です。安全のために、変更されたコピーを生成する操作を使用する必要があります。

于 2012-11-14T02:11:15.883 に答える
3

これは、参照の違いです。コードでは、内側の配列への参照を作成しますが、両方の場所で同じ値を参照しています。これは、両方の配列を呼び出すことで確認できObject#object_idます (一方の参照を介して値を変更し、もう一方の参照から変更されていることを確認するだけでは十分ではありません!)。

space = [  [ [1],[2],[3] ],  [ [4],[5],[6] ],  [ [70],[8],[9] ]  ]
=> [[[1], [2], [3]], [[4], [5], [6]], [[70], [8], [9]]] 
space[2][1].object_id
=> 70329700053380 
space_subset = space[(1..2)].collect { |y| y[1] }
=> [[5], [8]] 
space_subset[1].object_id
=> 70329700053380

残念ながら、オブジェクトの「浅い」コピーしか作成Array#dupArray#cloneないため、操作するコピーを取得するには、少し回避策を使用する必要がありますspace。ディープ コピーを取得する簡単な方法の 1 つは、次のとおりです。

Marshal.load(Marshal.dump(space))

また、再帰関数を作成して取得spaceし、手動で新しい配列にコピーすることもできます。

そしてそれを証明するために:

space = [  [ [1],[2],[3] ],  [ [4],[5],[6] ],  [ [70],[8],[9] ]  ]
=> [[[1], [2], [3]], [[4], [5], [6]], [[70], [8], [9]]]
space[2][1].object_id
=> 70329700053380
space_subset = Marshal.load(Marshal.dump(space))
=> [[[1], [2], [3]], [[4], [5], [6]], [[70], [8], [9]]]
space_subset = space_subset[(1..2)].collect { |y| y[1] }
=> [[5], [8]]
space_subset[1].object_id
=> 70329695297500
space_subset[1].delete(8)
=> 8
space
=> [[[1], [2], [3]], [[4], [5], [6]], [[70], [8], [9]]]
space_subset
=> [[5], []]

それが役立つことを願っています!

于 2012-11-14T02:24:51.413 に答える