多重ループから抜ける in Ruby | ラブレタ ー それから

多重ループから抜ける in Ruby

どんなプログラムでも多重ループを使う事はよくあること。

それで、一番深いところから(中途半端に深いところからでもいいが)抜けたい事があると思います。


昔のVisual Basicとかでは「GoTo」とか使いましたが、今は使えても使わないほうがいいという事が一般的に常識になっています。


そもそも、多重ループを一気に抜けるというアルゴリズムを見直したほうがいい場合もありますが。。。


しかしながら、やはり、「Ruby」ではどうするのか、いろいろ調べてみました。


結論から。


Rubyの場合は、こんな感じが一番いいようです。


01 : catch(:exit) { 02 : 5.times{|i| 03 : (i + 1).times{|j| 04 : k = i * 10 + j 05 : throw :exit if k >= 30 06 : puts i * 10 + j 07 : } 08 : puts "---\n" 09 : } 10 : } 11 : 12 : puts "END"

結果は

0 --- 10 11 --- 20 21 22 --- END

つまり

catch(シンボル) { 処理1 throw シンボル if 条件 処理2: } 処理3

ということになります。



最初は「break」を使って、

01 : sw = false 02 : 03 : 5.times{|i| 04 : (i + 1).times{|j| 05 : k = i * 10 + j 06 : if k >= 30 07 : sw = true 08 : break 09 : end 10 : puts i * 10 + j 11 : } 12 : break if sw 13 : puts "---\n" 14 : } 15 : 16 : puts "END"
てな感じでやろうと思いましたが、breakは一つのループしか抜けることができません。
それで、「sw」というTrueClassの変数を使い上のループでそれを判断してもう一つ上のループを抜けます。

※Javaなどではラベルを使って「break」で一気にループを抜けることもできるのですが、Rubyの場合はラベルが使えないようです。

しかし、これは全てのループで「sw」の条件判断しなければならず、無駄な処理になってしまいます。
第一、見た目もシンプルでない。

だいたいプログラムにおいて、見た目もシンプルでないものは実際無駄も多いです。

次は、メソドを使って、
01 : def loopExit 02 : 5.times{|i| 03 : (i + 1).times{|j| 04 : k = i * 10 + j 05 : return if k >= 30 06 : puts i * 10 + j 07 : } 08 : puts "---\n" 09 : } 10 : end 11 : 12 : loopExit 13 : 14 : puts "END"/blockquote>
てな感じで、「return」でメソド自体を脱出する。

しかし、これも多重ループのたびにメソドを作るのも無駄ですし、プログラム処理を追っかけるのが面倒。
これも、見た目もシンプルでない。

次は、「begin~raise~resucue~end」を使い、
01 : begin 02 : 5.times{|i| 03 : (i + 1).times{|j| 04 : k = i * 10 + j 05 : raise if k >= 30 06 : puts i * 10 + j 07 : } 08 : puts "---\n" 09 : } 10 : rescue 11 : end 12 : 13 : puts "END"
てな感じ。
「raise」で例外をわざと発生させ、それを受け止める「rescue」では何もしない。
それに、「raise」以外の本当の例外が発生した時もループを抜けてしまう。
「raise 例外クラス」を利用すればいけそうであるが、わざわざコード書くのも面倒。

これも、見た目もシンプルでない。


で、結果、いろいろ調べたら一番上の「catch~throw」に行き着きました。

しかし、これ、「Rubyリファレンス」の「ルビーの落とし穴 > trap::スコープ、制御構造 > 多重ループを抜ける」にありましたな~。
http://www.ruby-lang.org/ja/man/html/trap_A5B9A5B3A1BCA5D7A1A2C0A9B8E6B9BDC2A4.html

一通り読まないと。