【问题标题】:Weird behavior on new (nested) Array in Ruby [duplicate]Ruby中新(嵌套)数组的奇怪行为[重复]
【发布时间】:2012-08-10 02:38:03
【问题描述】:

为什么两段代码没有打印相同的内容。我打算让第一件产生第二件的输出

a=Array.new(5,Array.new(3))
for i in (0...a[0].length)
  a[0][i]=2
end
p a

# 这将打印 [[2, 2, 2], [2, 2, 2], [2, 2, 2], [2, 2, 2], [2, 2, 2]]*

a=Array.new(5).map{|d|d=Array.new(3)}
for i in (0...a[0].length)
  a[0][i]=2
end
p a

# 打印 [[2, 2, 2], [nil, nil, nil], [nil, nil, nil], [nil, nil, nil], [nil, nil, nil]]

【问题讨论】:

  • 无需在您的区块中分配给d。它应该是...map{|d| Array.new(3)}
  • @meagar:根本不需要|d|...
  • 您能否解释一下您对Array::new 的文档准确地 不清楚的地方?这样,Ruby 开发人员可以改进文档,以便未来的开发人员不会像您一样陷入同样的​​陷阱。请帮助让世界变得更美好!

标签: ruby


【解决方案1】:

这个

a=Array.new(5,Array.new(3))

创建一个数组,其中包含五次相同的数组对象。有点像这样做:

a = []
b = a
a[0] = 123
puts b[0] #=> 123

这个在哪里:

a=Array.new(5).map{ Array.new(3) }

为父数组中的每一项创建一个新的 3 项数组。因此,当您更改第一个项目时,它不会触及其他项目。

这也是为什么您不应该真正使用 ArrayHash 构造函数默认参数的原因,因为它们并不总是按您预期的方式工作。

【讨论】:

  • 谢谢。为什么默认的 new 运算符行为如此奇怪......有点令人困惑:(
  • 提一下Array.new(5){Array.new(3)} 是OP 可能想要做的事情是个好主意。
  • @Marc-André Lafortune,你能详细说明为什么会这样吗?
  • [[nil]*3]*5 和 a=Array.new(5,Array.new(3)) 做同样的事情~~~这真的很烦人。怎么会?有什么解释吗?
  • 因为 Ruby 就是这样工作的。数组类定义了一个* 运算符。 * 运算符在传递整数 N 时,会在数组中生成数组的 N 副本。 [3] * 3 给你[3,3,3]。你已经将它包裹在[] 中,所以你有[[3,3,3]]。现在你将 outer 数组乘以 5,所以你得到了它的第一个元素 [3,3,3] 的 5 个副本,所以你最终得到了 [[3, 3, 3], [3, 3, 3], [3, 3, 3], [3, 3, 3], [3, 3, 3]]
【解决方案2】:
Array.new(5,Array.new(3))

在第一个示例中,您的数组包含对同一数组的 5 个引用。您使用 Array.new(3) 创建一个数组的单个实例,该引用用于您初始化的 5 个数组中的每一个。当你修改a[0][0]时,你也在修改a[1][0]a[2][0]等,它们都是对同一个数组的引用。

Array.new(5).map{ |d| Array.new(3) }

在第二个示例中,您的数组包含 5 个不同的数组。您的块被调用了 5 次,Array.new(3) 被调用了 5 次,并创建了 5 个不同的数组。 a[0]a[1] 等是不同的数组。

【讨论】:

  • 不需要什么?这是关于该特定代码行的行为的问题。必要与否无关紧要。
  • 哦,你是对的,对不起。所以让我提出一个改进建议:Array.new(5){Array.new(3)} 可能是 OP 想要的。
  • OP 想要什么并不重要,我不是在尝试更正他们的代码,而是像他问的那样尝试解释我去。
【解决方案3】:

以下是等价的:

Array.new(5,Array.new(3))

[Array.new(3)] * 5

inside = Array.new(3); [inside, inside, inside, inside, inside]

它们都会产生一个包含相同数组的数组。我的意思是 完全相同 对象。这就是为什么如果你修改它的内容,你会看到这个新值 5 次。

由于您需要独立数组,因此您要确保“内部”数组不是同一个对象。这可以通过不同的方式实现,例如:

Array.new(5){ Array.new(3) }

5.times.map { Array.new(3) }

Array.new(5).map { Array.new(3) }

# or dup your array manually:
inside = Array.new(3); [inside.dup, inside.dup, inside.dup, inside.dup, inside]

请注意,您首先使用的Array.new(5, []) 表单不会为您复制obj,它会重复使用它。由于这不是您想要的,您应该使用块形式Array.new(5){ [] },它将调用块 5 次,并且每次创建一个新数组。

Hash 类也有两个构造函数,是even more tricky

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-09-25
    • 2021-04-08
    相关资源
    最近更新 更多