我在RoR应用程序中调试了一个问题,并遇到了下面的代码(其中'map'是一个二维的整数数组)。代码尝试复制并附加每个子数组的最后一个元素:
map.each { |x| x << x[-1] }
在这行代码之前,
(rdb:29) p map
[[1, 2, 1, 3, 4], [1, 2, 2, 3, 4], [1, 2, 2, 3, 4], [1, 2, 2, 3, 4],
[1, 2, 2, 3, 4], [1, 2, 2, 3, 4]]
(rdb:29) p map.class
Array
(rdb:29) p map.first.class
Array
(rdb:29) p map.last.class
Array
后:
(rdb:29) p map
[[1, 2, 1, 3, 4, 4], [1, 2, 2, 3, 4, 4], [1, 2, 2, 3, 4, 4],
[1, 2, 2, 3, 4, 4], [1, 2, 2, 3, 4, 4, 4], [1, 2, 2, 3, 4, 4, 4]]
这里的问题是最后2个子数组附加了两个整数而不是一个整数,而前4个子数组是正确的。我更改了代码以使用Array.map,然后它正常工作:
map = map.map { |x| x + [x[-1]] }
总结一下: 我知道Array.each块中的迭代元素不应该被更改。但是为什么这样做会产生不可预测的结果呢?代码实际上大部分时间都在工作,有时会看到这个问题。它是Ruby或RoR中的错误吗?
答案 0 :(得分:2)
修改each
块内的子阵列不是你的问题,根本就没有错。您的问题是您的外部数组map
有时包含对同一子数组对象的多个引用。
考虑一下:
>> x = [[1, 2, 1, 3, 4], [1, 2, 2, 3, 4], [1, 2, 2, 3, 4]]
>> x.each { |a| a << a[-1] }
=> [[1, 2, 1, 3, 4, 4], [1, 2, 2, 3, 4, 4], [1, 2, 2, 3, 4, 4]]
每次结果都是一样的。但如果你有这个:
>> gotcha = [1, 2, 1, 3, 4]
>> x = [[1, 2, 1, 3, 4], gotcha, gotcha]
>> x.each { |a| a << a[-1] }
=> [[1, 2, 1, 3, 4, 4], [1, 2, 1, 3, 4, 4, 4], [1, 2, 1, 3, 4, 4, 4]]
然后,您将获得您所看到的额外尾随元素(每次),因为gotcha
被修改两次。第二种情况下的x
puts
与第一种情况下的x
相同,但它们不相同。
您的Array#map
方法始终有效,因为:
x + [x[-1]]
基本上复制x
,然后将x[-1]
附加到该副本,它永远不会修改x
,因此上面的gotcha
行为不会发生。
你无法摆脱指针,即使它们被称为引用也是如此。