如何在Ruby中跳出嵌套循环?

69

假设以下的 Ruby 代码:

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

NEXT BRANCH 当然是伪代码。有没有一种方法可以像Perl中那样(通过使用循环标签)跳出父循环?

提前感谢。


3
"NEXT BRANCH" 是一个非常糟糕的伪代码,用于跳出父循环。由于 "next" 和 "break" 是不同的关键字,因此需要改进此做法。 - Andrew Marshall
6个回答

115

catchthrow可能是你正在寻找的:

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

1
这是一种类似于“label”和“goto”的东西吗? - bfontaine
36
为了正确性,Ruby 中的 catchthrow 并不是用于错误处理。Ruby 使用 raiserescue 来处理错误。catchthrow 主要是为了跳出一个上下文环境。就像其他一些语言中的 goto 一样。 - Gurpartap Singh

20

没有内置的方法可以在不得到容器块许可的情况下跳出其中。你只能像这样做:

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

9
while c1
 while c2
    # execute code
    do_break = true if need_to_break_out_of_parent_loop
 end
 break if do_break
end

5

我的想法是将嵌套块移动到一个方法中,用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

1
其他帖子提到了一个类似创建“开关”变量的想法。请参见以下清晰的示例以了解其工作原理。请记住,第二个循环仍将运行,直到达到员工数组的末尾,但在开关被翻转后不会执行任何代码。如果您的员工数组很大,则这不是最优的方法,因为它可能会浪费不必要的时间。
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

在开关被切换后,内部数组将不再处于活动状态,并且父循环将移动到下一个分支并重置开关。显然,可以使用更多的开关来处理更复杂的情况。


1

编辑: 结果发现,可以通过在内部循环中调用break(仅终止该循环)来更简单地实现所需的效果:

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

这是使用 Ruby 的 begin-rescue-end 块编写的 @steenslag 想表达的意思:

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

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接