我正在尝试从数组中删除所有7
:
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]
这是怎么发生的?
答案 0 :(得分:7)
事实上只有大约一半(5/9)的物品消失了,这是一个死的赠品,在迭代整个集合时问题正在删除。
迭代将处理索引1,2,3,4等。如果在处理索引2时将其删除,则会将所有后续索引向下移动一次。
因此,当您在下一次迭代中转到索引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
个a.delete(7)
的原因,四个对中的一个和最终独立的一个。
删除单个给定值的所有项目的正确方法(在Ruby中)是使用array delete方法:
7
您还可以使用conditional delete来处理更复杂的情况,例如删除大于a.delete_if {|val| val > 7}
的所有内容:
a.to_enum.with_index.reverse_each do |item, index|
而且,如果你真的想自己做(作为一种教育练习),你只需要意识到问题是因为你以前进的方式处理数组 - 当你这样做时,超出您删除位置的更改可能会导致问题。
如果您要找到一种以向后方式处理数组的方法,则不会发生此问题。幸运的是,Ruby有这样一个野兽:
delete
该行将以删除不会影响将来操作的方式处理数组。请注意,如果您正在处理的数据结构不是简单的索引数组,那么在迭代时删除仍然会成为一个问题。
我仍然保证delete_if
和 String.xml
<string name="Message">Um eine neue europäische Friedensverordnung in Europa ins Leben zu rufen wurde auf dem Wiener Kongress am 8. Juni 1815 der Deutsche Bund gegründet. Der Bund bestand zwischen 1815 und 1866 mit</string>
android:text="@string/Message"
android:layout_height="wrap_content"
是正确的方法,因为它们已经融入Ruby,因此极不可能有bug。
答案 1 :(得分:3)
这是因为在每次迭代中您都在更新/删除正在循环的相同数组。
为此,您应该使用此delete方法:
a = [1,2,3,4,5,7,7,7,7,7,7,7,7,7,9,10,11,12,13]
a.delete(7)
# [1, 2, 3, 4, 5, 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 !
希望有所帮助:)
答案 2 :(得分:2)
一般情况下,从任何语言中迭代的数组中删除都会变得奇怪。要了解原因,请查看Array#each_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;
}
请注意,它实际上是从0到数组长度的for
循环。循环将从0计数到数组的原始长度。
当您从数组中删除某个项目时,其后的所有内容都会切换为1.如果i = 5
并且您致电a.delete_at(i)
,则表示a[6]
现在为a[5]
。 a[7]
现在是a[6]
。等等。下一次迭代将有i = 6
,这意味着您有效地跳过了一个元素。
举例说明,并假设您要删除2。
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]
请注意,最后两次迭代已离开数组,因为该数组现在比以前小两个元素。