Ruby 鲁比没有';t循环所有元素?
我正在尝试从数组中删除所有Ruby 鲁比没有';t循环所有元素?,ruby,Ruby,我正在尝试从数组中删除所有7s: a = [1,2,3,4,5,7,7,7,7,7,7,7,7,7,9,10,11,12,13] 我做到了: a.each_with_index do |item, index| if item == 7 a.delete_at(index) end end a # => [1, 2, 3, 4, 5, 7, 7, 7, 7, 9, 10, 11, 12, 13] 这是怎么发生的?这是因为在每次迭代中,您都在更新/删除循环使用的相同数
7
s:
a = [1,2,3,4,5,7,7,7,7,7,7,7,7,7,9,10,11,12,13]
我做到了:
a.each_with_index do |item, index|
if item == 7
a.delete_at(index)
end
end
a # => [1, 2, 3, 4, 5, 7, 7, 7, 7, 9, 10, 11, 12, 13]
这是怎么发生的?这是因为在每次迭代中,您都在更新/删除循环使用的相同数组 为此,应使用以下方法: 您可以通过运行以下程序查看问题:
a = [1,2,3,4,5,6]
a.each_with_index do |item, index|
puts "#{index} : #{item}"
if item == 4
a.delete_at(index)
end
end
输出:
0 : 1
1 : 2
2 : 3
3 : 4 # made delete here
4 : 6 # See the problem !
希望能有所帮助:)事实上,只有大约一半(5/9)的项目消失了,这是一个致命的漏洞,问题是在迭代集合时删除 迭代将处理索引1、2、3、4等等。如果在处理索引2时将其删除,则会将以后的所有索引下移1 因此,当您在下一次迭代中继续移动到索引3时,您将跳过原始索引3,因为它将被下移到索引2 换句话说,让我们从一个简单的示例开始,该示例包含两个要删除的连续项:
index | 0 | 1 | 2 | 3 |
value | 1 | 7 | 7 | 9 |
检查第一个索引,值为1
,因此不执行任何操作。然后检查第二个索引,其值为7
,因此删除它,给出:
index | 0 | 1 | 2 |
value | 1 | 7 | 9 |
然后检查第三个索引,其值为9
,因此不执行任何操作。你也到达了终点,所以它停止了
因此,您可以看到您实际上跳过了第二个要删除的项,因为您在迭代时改变了内容。这不是Ruby特有的问题,很多语言都有同样的问题
通常,每个完整的相邻项目对将仅删除该对中的第一个项目,而一个项目本身(后面没有相同值的另一个项目)将正常删除。这就是为什么只有5/9
的7
s被删除,四对中每对一个,最后一对是独立的
(在Ruby中)删除单个给定值的所有项的正确方法是使用以下方法:
您还可以用于更复杂的条件,例如删除大于7
的所有内容:
a.delete_if {|val| val > 7}
而且,如果你真的想自己做(作为一个教育练习),你只需要意识到问题是因为你以一种向前的方式处理数组-当你这样做时,超出你要删除的地方的更改可能会导致问题 如果要找到某种方法以向后方式处理数组,则不会出现此问题。幸运的是,Ruby有这样一只野兽:
a.to_enum.with_index.reverse_each do |item, index|
该行将以这样一种方式处理数组,即删除不会影响将来的操作。请注意,如果正在处理的数据结构不是简单的索引数组,则在迭代时删除仍然可能是一个问题
我仍然保证
delete
和delete\u if
是正确的方法,因为它们已经被烘焙到Ruby中,因此不太可能有bug。一般来说,从你正在迭代的数组中删除,用任何语言,都会变得很奇怪。要了解原因,请查看的代码
请注意,上两次迭代已经离开了数组,因为数组现在比以前小了两个元素。因为您在删除时looping@dnit13所以删除数组中所有7个的正确方法是?
a.delete(7)
当然是正确的方法,但是如果你想在迭代时删除,你必须这样做:b=a.dup;(a.size-1)。向下至(0)。如果b[j]==7,则在(j)处删除每个{j}b;b#=>[1,2,3,4,5,9,10,11,12,13]
。感谢您的解释,非常有用。如果我只想循环和删除怎么办?我写这个小程序是因为我试图从一个大的JSON数组中删除元素,但事情并没有像我所说的那样进行,所以我写这个程序是为了理解循环和删除是如何工作的Ruby@NamNamNam前面提到的数组#delete_如果
在引擎盖下循环并删除。感谢您的长回答和Ruby源代码!令人惊叹的!
a.delete_if {|val| val > 7}
a.to_enum.with_index.reverse_each do |item, index|
VALUE rb_ary_each_index(VALUE ary)
{
long i;
RETURN_SIZED_ENUMERATOR(ary, 0, 0, ary_enum_length);
for (i=0; i<RARRAY_LEN(ary); i++) {
rb_yield(LONG2NUM(i));
}
return ary;
}
i = 0
a = [1,2,2,2,3,4,5]
^
-------------------
i = 1
a = [1,2,2,2,3,4,5]
^
a.delete_at(i)
a = [1,2,2,3,4,5]
-------------------
i = 2
a = [1,2,2,3,4,5]
^
a.delete_at(i)
a = [1,2,3,4,5]
-------------------
i = 3
a = [1,2,3,4,5]
^
-------------------
i = 4
a = [1,2,3,4,5]
^
-------------------
i = 5
a = [1,2,3,4,5]
-------------------
i = 6
a = [1,2,3,4,5]