迭代时删除数组元素
我正在迭代一个嵌套数组, each
块有两个,并从内部迭代内的同一个数组中删除一个元素:
arr = [1,2,3] arr.each do |x| arr.each do |y| puts "#{arr.delete(y)}" end end
这产生结果3
。 数组变为[2]
。
为什么值2
没有传递给第一个或第二个循环? 这是嵌套迭代的某种副作用吗?
这是因为删除元素的索引。 我添加了一些输出来告诉你:
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 => []
它与嵌套无关。 实际上,只有内部循环才能获得相同的结果:
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]
,index
:0
,y
:1
其中index
是内部迭代所基于的索引,你删除了y
,你得到:
-
a
:[2, 3]
在下一个内部迭代中,您有:
-
a
:[2, 3]
,index
:1
,y
:3
注意,跳过2
是因为迭代基于索引( 1
)。 然后,删除3
,这给出:
-
a
:[2]
。
当外循环在索引1
处尝试下一次迭代时,a中没有足够的元素,因此它在那里结束。
为了理解这种情况,让我们来看一个使用索引遍历的简单数组。
你有一个[1,2,3]
的数组。
当您以0开始迭代时,当前元素为1.现在,您删除索引0处的元素1,您的数组将变为[2,3]
。
在下一次迭代中,您的索引将为1,并且将指向3.并且将删除3。 你的arrays将是[2]
。
现在,您的索引是2,数组的长度为1.因此,什么都不会发生。 现在,当这个内部循环完成时,外部循环将在更新的索引1处继续,然后返回到2.并且当数组的长度为1时,它们将不会被执行。
因此,通过这种方式,似乎使用索引作为迭代。
据我所知,它应该有未定义的行为(比如在C ++中不建议这样的代码)。 因为,在迭代时如果删除当前元素,它将破坏指针值(当前保存在传递给each
元素的function块的参数中)。
因为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
时循环