迭代时删除数组元素

时间:2016-02-19 07:08:36

标签: ruby

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

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

这会产生结果13。数组变为[2]

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

4 个答案:

答案 0 :(得分:9)

这是因为已删除元素的索引。我添加了一些输出来告诉你:

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(索引为0)。在删除2之后,索引为0,现在每次迭代都转到索引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
1
1: [2, 3], x: 3
3
=> [2]

我建议使用reverse_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
=> []

答案 1 :(得分:2)

它与嵌套无关。事实上,只有内循环才会得到相同的结果:

arr = [1,2,3]  
arr.each do |y|  
  puts "#{arr.delete(y)}"  
end  
# => outputs 1, 3
a # => [2]

编译是由于在迭代期间修改了数组。

原因是因为Array#each基于索引。首先,x变为1(这与结果完全无关)。在内循环中,首先你有:

  • a[1, 2, 3]index0y1

其中index是内部迭代所基于的索引,您删除y并得到:

  • a[2, 3]

在下一次内部迭代中,您有:

  • a[2, 3]index1y3

请注意,2被跳过,因为迭代基于索引(1)。然后,删除3,其中包含:

  • a[2]

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

答案 2 :(得分:1)

为了理解这种情况,让我们来看一个使用索引遍历的简单数组。

你有一个[1,2,3]的数组。

当您使用0开始迭代时,当前元素为1.现在,您删除索引0处的元素1,您的数组将变为[2,3]

在下一次迭代中,您的索引将为1,并且将指向3.并且将删除3。您的数组将为[2]

现在,您的索引是2,数组的长度为1.因此,什么都不会发生。 现在,当这个内部循环完成时,外部循环将在更新的索引1处继续,然后再到2.并且当数组的长度为1时,它们将不会被执行。

所以,顺便说一下,似乎是使用索引作为迭代。

据我所知,它应该有未定义的行为(比如在C ++中不建议这样的代码)。因为,在迭代时如果删除当前元素,它将破坏指针值(当前保存在传递给each的功能块的参数中)。

答案 3 :(得分:0)

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

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] 

因为你在循环期间删除,并且在每次迭代之后索引递增但是数组是一个元素short,因此,它删除了下一个(和所有)匹配元素,并且最后循环比较并在index >= length

时停止循环
相关问题