Как сломать вложенные циклы в Ruby? - PullRequest
46 голосов
/ 13 марта 2011

предполагается следующий код рубина:

bank.branches do |branch|
  branch.employees.each do |employee|
    NEXT BRANCH if employee.name = "John Doe"
  end
end

СЛЕДУЮЩИЙ ФИЛИАЛ, конечно, псевдокод.Есть ли способ, которым я могу выйти из родительского цикла, как это можно сделать в Perl, например (с помощью меток цикла)?

заранее спасибо.

Ответы [ 6 ]

84 голосов
/ 13 марта 2011

Catch и throw может быть тем, что вы ищете:

bank.branches do |branch|
  catch :missingyear do  #:missingyear acts as a label
    branch.employees.each do |employee|
      (2000..2011).each do |year|
        throw :missingyear unless something  #break out of two loops
      end
    end
  end #You end up here if :missingyear is thrown
end
16 голосов
/ 13 марта 2011

Нет встроенного способа вырваться из содержащих блоков без их согласия.Вам просто нужно сделать что-то вроде:

bank.branches do |branch|
  break unless branch.employees.each do |employee|
    break if employee.name == "John Doe"
  end
end
5 голосов
/ 29 сентября 2012
while c1
 while c2
    # execute code
    do_break = true if need_to_break_out_of_parent_loop
 end
 break if do_break
end
4 голосов
/ 02 июля 2013

Мой импульс - переместить вложенные блоки в метод с return вместо break.

def find_branch_and_employee_by_name(bank,emp_name)
  bank.branches.each do |branch|
    branch.employees.each do |employee|
      return([branch,employee]) if employee.name == emp_name
    end
  end
  nil   # employee wasn't found
end
0 голосов
/ 07 ноября 2014

Другие посты ссылались на идею, похожую на создание переменной "switch". Ниже приведен четкий пример того, как это работает. Имейте в виду, что второй цикл все еще будет работать до тех пор, пока не достигнет конца массива employee, но не выполнит никакого кода после переключения переключателя. Это не оптимальный способ сделать это, так как он может быть излишне дорогостоящим, если ваш массив сотрудников большой.

def workforce
  bank.branches do |branch|
    switch = 0
    branch.employees.each do |employee|
      if switch == 1
       next
      end  
      if employee.name = "John Doe"
       switch = 1
      end
   end
 end

После переключения переключателя внутренний массив больше не будет активным, и родительский цикл переместится в следующую ветвь с сбросом переключателя. Очевидно, что для более сложных случаев можно использовать больше переключателей.

0 голосов
/ 21 октября 2014

Редактировать: Получается, что желаемый эффект может быть достигнут гораздо проще, если вызвать разрыв внутри внутреннего цикла (только завершит этот цикл):

bank.branches do |branch|
  branch.employees.each do |employee|
    break if employee.name = "John Doe"
  end
end

Здесьчто @ steenslag было написано с помощью блока Ruby begin-rescue-end:

letters = [%w(a b c),%w(d e f),%w(g h i)]
# => [["a", "b", "c"], ["d", "e", "f"], ["g", "h", "i"]]

letters.each do |trine|
  begin
    trine.each do |letter|
      raise "Breaking out of inner cycle." if letter == "e"
      puts letter
    end
  rescue
    next
  end
end
# => abcdghi

Итак, ваш пример:

bank.branches do |branch|
  branch.employees.each do |employee|
    begin
      raise "Breaking out of inner cycle." if employee.name = "John Doe"
    rescue
      next
    end      
  end
end
...