Ruby数组的奇怪行为?

时间:2016-08-13 16:30:59

标签: ruby

我正在使用Ruby 2.3.1,我无法判断我是否遇到过错误或者这是否是预期的行为。

如果通过制作嵌套数组来创建NxN矩阵,那么:

matrix = [[0]*5]*5

然后在对角线上设置元素,如下:

(0..4).each {|i| matrix[i][i] = i}

这最终影响到每一行中的每一列:

[
 [0, 1, 2, 3, 4],
 [0, 1, 2, 3, 4],
 [0, 1, 2, 3, 4],
 [0, 1, 2, 3, 4],
 [0, 1, 2, 3, 4]
]

这是预期的行为吗?

P.S。我不想使用Ruby的Matrix库,而是宁愿使用普通数组。

提前致谢:)

3 个答案:

答案 0 :(得分:1)

在Ruby中,数组在幕后是array类型的对象,它们可以包含基本类型和对其他对象的引用。现在,最后一点很重要 - 数组不包含对象本身,而是指向它的指针,当程序员要求时,它被解释为必要的。

OP的原始初始化代码

matrix = [[0]*5]*5

真正创建一个包含5个0的array个对象,然后将指针复制到它5次。

时也会发生这种情况
matrix = Array.new(5, Array.new(5, 0))

出于同样的原因。因此,正如在评论中发布的那样,用于创建5个不同array对象的数组的惯用正确的Ruby方法是

matrix = Array.new(5){Array.new(5, 0)}

这会产生一个包含指向5个不同 array个对象的指针的数组,从而防止OP遇到问题。有关Ruby数组行为的完整文档可以在finely-crafted link找到。

答案 1 :(得分:1)

您无需更改对角线即可观察到该行为;只需改变任何元素,比如说

matrix[1][1] = 1

然后

matrix
  #=> [[0, 1, 0, 0, 0], [0, 1, 0, 0, 0], [0, 1, 0, 0, 0],
  #    [0, 1, 0, 0, 0], [0, 1, 0, 0, 0]] 

考虑

matrix.map { |row| row.object_id }
  #=> [70153694327100, 70153694327100, 70153694327100,
  #    70153694327100, 70153694327100].

这表明matrix的所有元素(“行”)都是同一个对象,因为如果更改了该对象,matrix的所有元素都会受到影响。 matrix = [[0]*5]*5相当于

matrix = Array.new(5, Array.new(5,0))

(参见Array::new,尤其是“常见陷阱”。)你想要的是什么(如@Sebastian所说)

matrix = Array.new(5) { Array.new(5,0) }
  #=> [[0, 0, 0, 0, 0], [0, 0, 0, 0, 0], [0, 0, 0, 0, 0],
  #    [0, 0, 0, 0, 0], [0, 0, 0, 0, 0]]

这样

matrix[1][1] = 1

仅影响那一个元素:

matrix
  #=> [[0, 0, 0, 0, 0], [0, 1, 0, 0, 0], [0, 0, 0, 0, 0],
  #    [0, 0, 0, 0, 0], [0, 0, 0, 0, 0]] 

答案 2 :(得分:0)

matrix = [[0]*5]*5

让我们打破这个:

a = [0]*5

创建一个包含5个零的数组;这是一个整数数组。

matrix = [a] * 5

为同一个数组a创建一个包含5个引用的数组。

当然,当你修改一个时,其他的将被修改;它是相同的数组。

我不认识Ruby,所以请随意纠正任何不正确的术语。