Как да се прекъсне от вложени цикли в Ruby?

приемете следния ruby ​​код:

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

NEXT BRANCH разбира се е псевдокод. има ли начин да изляза от родителски цикъл, както може да се направи в Perl, например (чрез използване на етикети на цикъл)?

Благодаря предварително.


person crlsrns    schedule 13.03.2011    source източник
comment
NEXT BRANCH е много лош псевдокод за излизане от родителски цикъл. Тъй като next и break са различни ключови думи.   -  person Andrew Marshall    schedule 13.03.2011


Отговори (5)


Хвани и хвърли може да е това, което търсите:

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
person steenslag    schedule 13.03.2011
comment
Дали е вид label и goto? - person bfontaine; 12.05.2012
comment
За коректност тук, catch и throw не са за грешки в Ruby. Ruby има raise и rescue за прихващане на грешки. хващане и хвърляне всъщност е просто излизане от контекст. Точно като goto на някои други езици. - person Gurpartap Singh; 14.08.2012

Няма вграден начин за излизане от съдържащите се блокове без тяхното съгласие. Просто ще трябва да направите нещо като:

bank.branches do |branch|
  break unless branch.employees.each do |employee|
    break if employee.name == "John Doe"
  end
end
person Chuck    schedule 13.03.2011

Моят импулс би бил да преместя вложените блокове в метод с 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
person Stovey    schedule 02.07.2013

Редактиране: Оказва се, че желаният ефект може да бъде постигнат много по-лесно чрез извикване на прекъсване във вътрешния цикъл (само ще прекрати този цикъл):

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

Ето какво получаваше @steenslag, написано с Ruby begin-rescue-end block:

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
person Epigene    schedule 21.10.2014

Други публикации споменават идея, подобна на създаването на променлива "switch". Вижте по-долу за ясен пример как работи. Имайте предвид, че вторият цикъл все още ще работи, докато достигне края на масива на служителите, но няма да изпълни никакъв код, след като превключвателят бъде обърнат. Това не е оптималният начин да направите това, тъй като може да бъде ненужно скъпо време, ако вашият набор от служители е голям.

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

След като превключвателят бъде обърнат, вътрешният масив вече няма да бъде активен и родителският цикъл ще се премести към следващия клон с нулирането на превключвателя. Очевидно повече превключватели могат да се използват за по-сложни случаи.

person MusicIsFreedom    schedule 06.11.2014