k = [1,2,3,4,5]
for n in k
puts n
if n == 2
k.delete(n)
end
end
puts k.join(",")
# Result:
# 1
# 2
# 4
# 5
# [1,3,4,5]
# Desired:
# 1
# 2
# 3
# 4
# 5
# [1,3,4,5]
Этот тот же эффект происходит с другим итератором массива, k.each:
k = [1,2,3,4,5]
k.each do |n|
puts n
if n == 2
k.delete(n)
end
end
puts k.join(",")
имеет тот же вывод.
Причина это происходит, довольно ясна... Ruby на самом деле не выполняет итерации через объекты, хранившие в массиве, а скорее просто превращает его в итератор довольно индекса массива, запускающийся в индексе 0 и каждый раз увеличивая индекс, пока это не закончено. Но когда Вы удаляете объект, он все еще увеличивает индекс, таким образом, он не оценивает тот же индекс дважды, к которому я хочу его.
Это не могло бы быть тем, что происходит, но лучше, чтобы я мог думать.
Существует ли очевидный способ, чтобы сделать это? Уже существует ли встроенный итератор, который может сделать это? Или я должен буду запачкать его и сделать итератор индекса массива и не увеличить, когда объект удален? (или выполните итерации через клон массива и удалите из исходного массива),
Разъяснение
Я просто не хочу удалять объекты из массива; извините, если это было ясно. То, что я хотел бы сделать, выполняют итерации через каждый элемент и "обрабатывают" его; этот процесс мог бы иногда удалять его. Быть более точным:
class Living_Thing
def initialize tracker,id
@tracker = tracker
@id = id
@tracker << self
end
def process
do_stuff
puts @id
if @id == 2
die
end
end
def die
do_stuff_to_die
@tracker.delete(self)
end
def inspect
@id
end
end
tracking_array = Array.new()
foo = Living_Thing.new(tracking_array,1)
bar = Living_Thing.new(tracking_array,2)
rab = Living_Thing.new(tracking_array,3)
oof = Living_Thing.new(tracking_array,4)
puts tracking_array.join(",") # => [1, 2, 3, 4]
for n in tracking_array
n.process
end
# result: only foo, bar, and oof are processed
Идеально, я хотел бы, чтобы все объекты в tracking_array были обработаны.
Когда Living_Thing удален из tracking_array, Living_Thing#die нужно назвать; do_stuff_to_die очищает вещи, которые должны быть claned.
это может быть более подходящим для обработки (Ref. updated clarification)
k = [1,2,3,4,5]
k.dup.each do |n|
puts n
if n == 2
k.delete(n)
end
end
puts k.join(",")
это обходит вопрос, который вы задали (об итерации через объекты против итерации через индексы)
В большинстве языков ошибка при изменении коллекции при ее повторении. В большинстве языков решение состоит в том, чтобы создать копию и изменить ее или создать список индексов и выполнить операции с этими индексами, когда вы закончите итерацию, но итераторы Ruby дают вам немного больше, чем это. Пара решений очевидна. Самый идиоматичный IMO:
puts k
puts k.reject {|n| n == 2}.join(',')
Более прямо переведено из вашего примера:
k.delete_if do |n|
puts n
n == 2
end
puts k.join(',')
( delete_if
- это в основном деструктивная версия reject
, которая возвращает массив объектов, которые блокируются не вернул истину для.)
Хорошо, допустим, вы хотите удалить все двойки из своего массива:
arr = [1,2,3,4,5]
arr.delete(2)
puts arr.join(", ")
# => "1, 3, 4, 5"
arr = [1,2,3,2,4,2,5,2]
arr.delete(2)
puts arr.join(", ")
# => "1, 3, 4, 5"
Но я подозреваю, что вы хотите повторить итерацию, поэтому я бы:
arr = [1,2,3,4,5]
arr.each {|x| a[a.index(x)] = nil if x == 2}.compact!
Может, это слишком грязно? Присваивание nil сохраняет количество итераторов правильным, а компактно!
стирает нули постфактум. Карта курса
делает его немного короче и чище:
arr.map {|x| x if x != 2}.compact!