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

时间:2010-12-20 00:16:32

标签: ruby arrays hash

我想使用相同的键但不同的值复制哈希。我编写了以下代码片段,并遇到了一些我没想到的事情:

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 个答案:

答案 0 :(得分:3)

如果Array#*复制了数组的元素,那么当在具有不可复制元素(其中包括数字)的数组上使用时,它会中断,这是不可取的。

至于如何做你想做的事:用hsh.keys.zip([[]] * hsh.length)替换hsh.map {|k,v| [k, []] }

答案 1 :(得分:1)

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

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

例如:

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

=&GT; [2149128060,2149128060,2149128060]

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

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

=&GT; [2149106700,2149106660,2149106640]