我试图理解为什么以下代码奇怪地表现出来(据我所知)。
[1] pry(main)> a = {}
=> {}
[2] pry(main)> a[1] = [[0,0]] * 7
=> [[0, 0], [0, 0], [0, 0], [0, 0], [0, 0], [0, 0], [0, 0]]
[3] pry(main)> a[2] = [[0,0]] * 7
=> [[0, 0], [0, 0], [0, 0], [0, 0], [0, 0], [0, 0], [0, 0]]
[4] pry(main)> a[1][2][0] = 3 # Should be one value changed, right?
=> 3
[5] pry(main)> a
=> {1=>[[3, 0], [3, 0], [3, 0], [3, 0], [3, 0], [3, 0], [3, 0]],
2=>[[0, 0], [0, 0], [0, 0], [0, 0], [0, 0], [0, 0], [0, 0]]}
我认为应该发生的是,索引a
的密钥1
的哈希2
中的数组的一个值应该更改为3
,而是全部整个数组的第一个值更改为3
。这里发生了什么,我错过了什么?这是我的Ruby版本。
$ ruby -v
ruby 2.3.1p112 (2016-04-26 revision 54768) [x86_64-linux]
编辑:
我也试过以下
[1] pry(main)> a = {}
=> {}
[2] pry(main)> a[1] = ([[0,0].dup].dup * 7).dup
=> [[0, 0], [0, 0], [0, 0], [0, 0], [0, 0], [0, 0], [0, 0]]
[3] pry(main)> a[2] = ([[0,0].dup].dup * 7).dup
=> [[0, 0], [0, 0], [0, 0], [0, 0], [0, 0], [0, 0], [0, 0]]
[4] pry(main)> a[1][2][0] = 3
=> 3
[5] pry(main)> a
=> {1=>[[3, 0], [3, 0], [3, 0], [3, 0], [3, 0], [3, 0], [3, 0]],
2=>[[0, 0], [0, 0], [0, 0], [0, 0], [0, 0], [0, 0], [0, 0]]}
[6] pry(main)> a = {}
=> {}
[7] pry(main)> a[1] = ([[0,0].clone].clone * 7).clone
=> [[0, 0], [0, 0], [0, 0], [0, 0], [0, 0], [0, 0], [0, 0]]
[8] pry(main)> a[2] = ([[0,0].clone].clone * 7).clone
=> [[0, 0], [0, 0], [0, 0], [0, 0], [0, 0], [0, 0], [0, 0]]
[9] pry(main)> a
=> {1=>[[0, 0], [0, 0], [0, 0], [0, 0], [0, 0], [0, 0], [0, 0]],
2=>[[0, 0], [0, 0], [0, 0], [0, 0], [0, 0], [0, 0], [0, 0]]}
[10] pry(main)> a[1][2][0] = 3
=> 3
[11] pry(main)> a
=> {1=>[[3, 0], [3, 0], [3, 0], [3, 0], [3, 0], [3, 0], [3, 0]],
2=>[[0, 0], [0, 0], [0, 0], [0, 0], [0, 0], [0, 0], [0, 0]]}
当然值应该是副本吗?
答案 0 :(得分:3)
a[1]
中的所有元素都引用相同的数组。
[0,0]
后, [[0,0]] * 7
未被深层复制。
解决方案:a[1] = Array.new(7) { [0,0] }
(感谢@Stefan!)
答案 1 :(得分:0)
以下是如何诊断此问题:
object_id
会告诉你Ruby保存对象的“插槽”。对象应具有唯一ID:
foo = []
bar = []
foo.object_id # => 70322472940660
bar.object_id # => 70322472940640
如果他们不这样做,虽然他们可能是不同的变量名,但他们仍然指向同一个对象。这在更高级的用途中可能是理想的,但通常它不是你想要的东西,因为改变一个会改变另一个会导致混乱,咬牙切齿并可能回归“不应该被命名的人”。例如,当我们看到时,我们会被愚弄:
[[]] * 2 # => [[], []]
因为看起来我们正在找回两个阵列,可以使用并行分配来分配,例如:
foo, bar = [[]] * 2
但是,当我们查看foo
和bar
以查看它们存储的位置时,我们可以看到问题,它们都指向相同的插槽,因此更改其中一个会更改另一个:
foo.object_id # => 70323794190700
bar.object_id # => 70323794190700
foo << 1
bar # => [1]
编写代码时,这是一个古老的问题;您必须了解“按值传递”与“按引用传递”以及当某种语言正在执行某项操作时,否则会出现此问题。然后你必须学习如何告诉语言以避免这个问题。