为什么在Ruby中,数组*引用*值而不是复制?

3
我想要复制一个哈希表,使用相同的键但不同的值。我编写了以下代码片段,并遇到了一个意外情况:
hsh = {:foo => 'foo', :bar => 'bar'}

hsh_copy = Hash[hsh.keys.zip([[]] * hsh.length)] # => {:foo=>[], :bar=>[]}
hsh_copy[:foo] << 1
hsh_copy[:bar] << 2

hsh_copy # => {:foo=>[1, 2], :bar=>[1, 2]}

使用 * 运算符时,似乎并没有复制嵌套数组,而是继续引用第一个数组。
如果有人能解释为什么会发生这种情况,我将非常高兴。此外,欢迎提供更好的哈希复制方法,但我更关心的是了解为什么在这里 * 不起作用。
2个回答

3
如果Array#*复制了数组的元素,那么在使用具有不可复制元素(包括数字等)的数组时,它将会出现错误,这是不可取的。
至于如何做你想做的事情:用hsh.map {|k,v| [k, []] }替换hsh.keys.zip([[]] * hsh.length)

谢谢您的回答 - 看起来 Ruby 文档有些误导,因为它明确表示“复制”。但是按照同样的逻辑,为什么以下代码不会替换所有 ['a']['d'],而只是第一个呢?arr = [['a'], ['b'], ['c']] * 3; arr[0] = ['d']。然后是这个:arr = [[], [], []] * 3; arr[0] = {} - vonconrad
@vonconrad:因为在这些情况下,你并没有改变第一个数组元素,而是设置了一个新的。这也是为什么 a = "a"; b = a; b.replace("b") 会同时改变 ab,而 a = "a"; b = a; b = "b" 不会改变它们两个的原因。 - sepp2k
@vonconrad:我不认为文档有误导性,尽管这是可以理解的误读。文档中说“self的副本”——也就是说,该方法将数组复制n次。文档中没有说数组中的项目被复制。问题在于你假设进行深度复制,但实际上该方法执行浅复制(这是大多数语言中数组复制方法的默认值)。 - Chuck

1

* 运算符将数组的副本拼接在一起以满足新长度。

如果数组元素引用对象,则复制时实际上创建了一个新的数组元素,但它是引用相同对象的新数组元素。

例如:

irb(main):012:0> ([[]] * 3).map { |e| e.object_id }

=> [2149128060, 2149128060, 2149128060]

在您的情况下,您可以使用.map创建新元素,并让Ruby每次都使用[]创建一个新对象,但对于一般解决方案,请从以下开始:

irb(main):013:0> ([[]] * 3).map { |e| e.clone.object_id }

=> [2149106700, 2149106660, 2149106640]

=> [2149106700,2149106660,2149106640]


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接