或るプログラマの開発日記

日々の勉強したことの備忘録なんかに使っていきます

Rubyで多重ループを抜ける方法

Rubyの大域脱出に関するメモ。

ネストが深いループから何かの条件で一気に抜けたい場合、Rubyではcatch-throwを使うのが適切なようです。

書き方

以下が大域脱出のサンプル。如何にもサンプルの為のサンプルという感じですが、ネストしたループから抜けているのがお分かりいただけるかと。

catch(:break_loop) do
  (1..5).each do |x|
    (1..5).each do |y|
      puts "x=%d, y=%d" % [x,y]
      throw :break_loop  if x * y > 10
    end
  end
end

実行結果

x=1, y=1
x=1, y=2
x=1, y=3
x=1, y=4
x=1, y=5
x=2, y=1
x=2, y=2
x=2, y=3
x=2, y=4
x=2, y=5
x=3, y=1
x=3, y=2
x=3, y=3
x=3, y=4

JavaC++に慣れた人だと、catch節には例外処理を書くものだというイメージがあったりするのですが、Rubyの場合はその辺は切り離して考えたほうが良さそうです。

オブジェクトを返すこともできます

catch内部に定義した変数を返却する事も可能。こういう書き方を覚えておくと何かの時に役に立つのかもしれません。

(a,b,c) = catch(:break_loop) do
  (1..20).each do |x|
    (1..20).each do |y|
      (1..20).each do |z|
        throw(:break_loop, [x,y,z])  if x * y * z > 1000
      end
    end
  end
end
puts "a=%d, b=%d, c=%d" % [a,b,c]#=>a=3, b=17, c=20

雑感

RubyはGOTO文がありませんので、上の例以外に一気にネストを抜ける為には例外をraiseするか、メソッドに分割してreturnする方法ぐらいしかありませんね。