Рубиновая петля, пропускающая элементы - PullRequest
3 голосов
/ 10 февраля 2012

У меня есть это:

puts pids
pids.each do |pid|
    puts "Running on pid #{pid}"
    begin
        Process::kill(0, pid)
        puts "Pid #{pid} still alive"
    rescue Errno::ESRCH
        puts "Pid #{pid} now dead!!!!"
        pids.delete(pid)
        running_jobs -= 1
        puts "Remaining jobs: #{running_jobs}"
    end
end

, который выводит это:

25555
25579
25616
Running on pid 25555
Pid 25555 now dead!!!!
Remaining jobs: 2
Running on pid 25616
Pid 25616 now dead!!!!
Remaining jobs: 1    

Как видите, цикл никогда не выполняется на среднем элементе. Может кто-нибудь сказать мне, почему это происходит таким образом? Мне нужно перебрать каждый элемент и обработать его соответствующим образом.

Ответы [ 3 ]

6 голосов
/ 10 февраля 2012

Вы изменяете массив при его итерации по нему с pids.delete(pid) Таким образом, вы находитесь на 25555 с индексом 0, затем удаляете его, в результате чего массив выглядит как [25579, 25616]. Затем вы выполняете итерацию, теперь вы находитесь с индексом 1, который равен 25616. Если вы просто удалите удаление, вы больше не будете изменять массив, и итерация будет работать, как и ожидалось.

4 голосов
/ 10 февраля 2012

Таким образом, ваш цикл находится, например, в пункте 5, и вы удаляете его. Элементы 6, 7 и т. Д. Перемещаются вверх на 1, и когда ваш цикл идет туда, где раньше был элемент 6, он находит элемент 7.

Но говорить о том, что вы не можете изменить свой массив во время итерации по нему, немного сложно. Вы можете сохранить свой код в точности так, как он есть, если вы просто замените свой each на reverse_each. Это перебирает массив от конца к началу, поэтому теперь, когда вы удаляете элемент 5, не имеет значения, что 6 перемещается вверх, потому что вы переходите к 4, а это не изменилось (пока).

3 голосов
/ 10 февраля 2012

Вы не можете изменить массив во время итерации по нему - просто удалите эту строку:

pids.delete(pid)

Судя по вашему коду, вы, вероятно, захотите провести рефакторинг таким образом, чтобы имитировать вызов delete:

pids = pids.select do |pid|
  puts "Running on pid #{pid}"
  begin
      Process::kill(0, pid)
      puts "Pid #{pid} still alive"
      true
  rescue Errno::ESRCH
      puts "Pid #{pid} now dead!!!!"
      running_jobs -= 1
      puts "Remaining jobs: #{running_jobs}"
      false
  end
end
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...