Вопрос

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]

Этот же эффект происходит с другой ITERATOR ARRAY, KHEACH:

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.

Когда Wiving_thing удаляется из Tracking_Array, Living_ting # Die должен называться; do_stuff_to_die очищает вещи, которые должны быть скреплены.

Это было полезно?

Решение

Это может быть более подходящим для обработки (Ref. Обновленное разъяснение)

k = [1,2,3,4,5] 
k.dup.each do |n| 
  puts n 
  if n == 2
    k.delete(n) 
  end 
end 
puts k.join(",")

Он бортовые шаги по вопросу, который вы имели, хотя (о итерации через объекты VS итерации через индексы)

Другие советы

Это ошибка на большинстве языков, чтобы мутировать коллекцию, пока вы его итерации. На большинстве языков решение состоит в том, чтобы создать копию и мутировать, чтобы или создать список индексов и выполнять операции на тех показателях, когда вы закончите итераторы, но итераторы Рубины дают вам немного больше, чем это. Пара решений очевидно. Самый идиоматический 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, который возвращает массив объектов, которые блок не вернулся для.)

Хорошо, так что скажем, вы хотите устранить все 2 из вашего массива:

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 сохраняет подсчет итератора, а compact! вытирает нил после факта. Курс map Удерживает немного короче и чище:

arr.map {|x| x if x != 2}.compact!
Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top