Ruby 迭代时删除数组的元素

Ruby 迭代时删除数组的元素,ruby,Ruby,我正在迭代一个嵌套数组,每个数组有两个块,并在内部迭代中从同一数组中删除一个元素: arr = [1,2,3] arr.each do |x| arr.each do |y| puts "#{arr.delete(y)}" end end 这将产生结果1,3。该数组变为[2] 为什么值2没有传递给第一个或第二个循环?这是嵌套迭代的某种副作用吗?为了理解这种情况,让我们以一个简单的数组为例,使用索引进行遍历 您有一个带有[1,2,3]的数组 当您以0开始迭代时

我正在迭代一个嵌套数组,每个数组有两个
块,并在内部迭代中从同一数组中删除一个元素:

arr = [1,2,3]  
arr.each do |x|  
  arr.each do |y|  
    puts "#{arr.delete(y)}"  
  end  
end
这将产生结果
1
3
。该数组变为
[2]


为什么值
2
没有传递给第一个或第二个循环?这是嵌套迭代的某种副作用吗?

为了理解这种情况,让我们以一个简单的数组为例,使用索引进行遍历

您有一个带有
[1,2,3]
的数组

当您以0开始迭代时,当前元素是1。现在,删除索引0处的元素1,数组将成为
[2,3]

在下一次迭代中,您的索引将是1,它将指向3。和3将被删除。您的阵列将是
[2]

现在,索引为2,数组的长度为1。所以,什么也不会发生。 现在,当这个内部循环完成时,外部循环将在更新的索引1处恢复,然后恢复到索引2。由于数组的长度为1,因此将不执行它们

这样看来,似乎是在使用索引作为迭代


根据我的知识,它应该有未定义的行为(如C++中不推荐这样的代码)。因为,如果删除当前元素,则在迭代时会损坏指针值(当前保存在传递给

每个
的函数块的参数中)。

这是因为删除元素的索引。我添加了一些输出以向您展示:

arr = [1,2,3]  
arr.each do |x|  
  puts "1: #{arr.inspect}, x: #{x}"
  arr.each do |y|  
    puts "2: #{arr.inspect}, y: #{y}"
    puts "#{arr.delete(y)}"  
  end  
end
结果:

1: [1, 2, 3], x: 1
2: [1, 2, 3], y: 1
1
2: [2, 3], y: 3
3
=> [2]
1: [1, 2, 3], x: 1
1
1: [2, 3], x: 3
3
=> [2]
第一个删除的元素是内部每个块中的1(索引为0)。删除后,2的索引为0,现在每次迭代都会转到索引1,现在索引1是元素3。3将被删除,这就是迭代的结束。所以你得到[2]

如果没有嵌套的每个,也会发生相同的情况:

arr = [1,2,3]  
arr.each do |x|  
  puts "1: #{arr.inspect}, x: #{x}" 
  puts "#{arr.delete(x)}"  
end
结果:

1: [1, 2, 3], x: 1
2: [1, 2, 3], y: 1
1
2: [2, 3], y: 3
3
=> [2]
1: [1, 2, 3], x: 1
1
1: [2, 3], x: 3
3
=> [2]
我建议对此类操作使用
reverse\u each
,以避免这种行为:

arr = [1,2,3]  
arr.reverse_each do |x|  
  puts "1: #{arr.inspect}, x: #{x}" 
  puts "#{arr.delete(x)}"  
end
结果:

1: [1, 2, 3], x: 3
3
1: [1, 2], x: 2
2
1: [1], x: 1
1
=> []

这与筑巢无关。事实上,您只需使用内部循环即可获得相同的结果:

arr = [1,2,3]  
arr.each do |y|  
  puts "#{arr.delete(y)}"  
end  
# => outputs 1, 3
a # => [2]
这是由于在迭代过程中修改了数组造成的


原因是
Array#每个
都基于索引。首先,
x
变为
1
(这与结果完全无关)。在内部循环中,首先需要:

  • a
    [1,2,3]
    索引
    0
    y
    1
其中,
index
是内部迭代所基于的索引,删除
y
,得到:

  • a
    [2,3]
在下一个内部迭代中,您有:

  • a
    [2,3]
    索引
    1
    y
    3
请注意,
2
被跳过,因为迭代基于索引(
1
)。然后,
3
被删除,这将给出:

  • a
    [2]

当外部循环在索引
1
处尝试下一次迭代时,
a
中没有足够的元素,因此它在那里结束。

因为
每个
都使用索引进行迭代,并且您正在删除内部
循环中的一个元素,在下一次迭代中删除其他每个元素。如果您增加元素的数量,并在循环中包含迭代的当前索引,您将能够看到更大的画面

arr = [1,2,3,4,5,6,7,8,9]  
arr.each_with_index do |x,ix|  
  puts "loop1: #{arr.inspect}, x: #{x}, ix: #{ix}"
  arr.each_with_index do |y, iy|  
    puts "loop2: #{arr.inspect}, y: #{y}, iy: #{iy}"
    puts "#{arr.delete(y)}"  
  end  
end
结果

loop1: [1, 2, 3, 4, 5, 6, 7, 8, 9], x: 1, ix: 0
loop2: [1, 2, 3, 4, 5, 6, 7, 8, 9], y: 1, iy: 0
1
loop2: [2, 3, 4, 5, 6, 7, 8, 9], y: 3, iy: 1
3
loop2: [2, 4, 5, 6, 7, 8, 9], y: 5, iy: 2
5
loop2: [2, 4, 6, 7, 8, 9], y: 7, iy: 3
7
loop2: [2, 4, 6, 8, 9], y: 9, iy: 4
9
loop1: [2, 4, 6, 8], x: 4, ix: 1
loop2: [2, 4, 6, 8], y: 2, iy: 0
2
loop2: [4, 6, 8], y: 6, iy: 1
6
 => [4, 8] 

由于您在循环期间删除,并且在每次迭代之后,索引都会增加,但数组短了一个元素,因此,它会删除下一个(以及所有)可用的匹配元素,并且在循环结束时,当
索引>=长度

+1时,循环会比较并停止循环。或者您可以选择在这些情况下使用
select
reject
。您的第一段是您的意见/专题请求,应该与其他段落分开(可能放在Paranehese的末尾)。我认为这就是为什么尽管答案很简洁,但你没有被提升投票率的原因。您也没有提及外部循环(至少您需要解释它如何没有影响)。请批准适当的answer@bronislav,给点时间。才一个小时。“不用着急。”卡利斯沃夫兰,对不起