我有一个收集所有实例变量的类变量。它还有一种获取和删除它们的方法。
module Somemodule
class Widget
@@widgets = []
def initialize
...
@@widgets << self
end
def self.all
@@widgets
end
def delete
@@widgets.delete(self)
end
end
end
在我的代码中,我想有时删除小部件。
module Somemodule
def self.delete_bad_widgets
Widget.all.each do |widget|
if widget.bad
widget.delete
end
end
end
end
问题是这不会删除所有错误的小部件。如果我之后调用Widget.all,我仍然可以看到坏的小部件。任何人都知道发生了什么事吗?我正在运行Ruby 2.2.1。
谢谢
编辑:总而言之,大约有4500个小部件,代码会改变它们几次。但是我仍然可以运行类似下面的内容(在相同的delete_bad_widgets方法中!)并且仍然会看到错误的小部件:
Widget.all.each {|w| pp w if w.bad}
答案 0 :(得分:3)
如果在每个条目中删除条目,则会影响循环。
请看这个例子:
a = [1,2,3,4,5,6,7]
p a
a.each{|x|
p x
a.delete(x) if x == 3
}
p a
结果:
[1, 2, 3, 4, 5, 6, 7]
1
2
3
5
6
7
[1, 2, 4, 5, 6, 7]
不打印条目4! 每个循环保留索引,但序列随删除而改变。
我建议实现类方法delete_bad
或delete_if
。
答案 1 :(得分:2)
尝试使用reject!
删除不良小部件,而不是自己进行迭代。
module Somemodule
def self.delete_bad_widgets
Widget.all.reject! {|widget| widget.bad}
end
end
正如knut的回答所指出的那样,在遍历数组时使用delete会导致某些元素被跳过。在迭代该集合的同时插入/删除集合的元素通常不是一个好主意。
此外,使用reject!
(又名delete_if
)会表现得更好,因为delete
将在每次调用时基本遍历整个数组(即reject!
给出O(n)表现在哪里 - 您使用它的方式 - delete
将给出O(n ^ 2)最差情况表现。)