LoginSignup
0
0

More than 5 years have passed since last update.

ネストしたループ構造の内側から外側のループをredoする

Last updated at Posted at 2017-01-28

久しぶりの投稿です。相変わらず Rubyist です。よろしくお願いします。

なぜ内側から外側のループを redo したいの?

内側ループの中でエラーが生じたときに、外側ループを同じループでやり直しをさせたいことがあったのです。

まずは redo の復習

redo って何だっけ?滅多に使わないから忘れるよね。

redo

例:

redo

文法:

redo

ループ条件のチェックを行なわず、現在の繰り返しをやり直します。
制御構造 (Ruby 2.4.0)

常に redo するなんてあり得ないので if 修飾子つけたり (redo if ~~) など、何かの条件を付けて書きますよね。

あと念のためですが

ループとは

  • while
  • until
  • for
  • イテレータ

のいずれかを指します。

ループ構造をネストさせると redo はどこをやり直す?

さきほどの記載にも

現在の繰り返しをやり直します。

とあったように、いまの繰り返し(ループ)をやり直します。つまりは最も内側のループをやり直します。

外側のループを redo したいのだけど

大域脱出(catchthrow)を使います。
具体的には Kernel.#catch のブロックを目的のループのすぐ内側に作ります。

catchthrow の復習

result = catch do |tag|
  for i in 1..2
    for j in 1..2
      for k in 1..2
        throw tag, k
      end
    end
  end
end

p result #=> 1

module function Kernel.#catch (Ruby 2.4.0)


throw は同じ tag を指定した catch のブロックの終わりまでジャンプします。
module function Kernel.#throw (Ruby 2.4.0)

上記の例では、throw tag, k によって catch ブロックの終わりの end までジャンプします。throw の第二引数 kcatch ブロックの返値になるので、変数 result1 になっています。

では実際に外側ループの redo をやってみよう

外側ループを5周させますが、内側ループ(3周)の間にエラーが生じたら外側ループをやり直しさせます。

5.times do |i|
  redo_flag = catch :redo_outer_loop_from_deep_loop do
    3.times do |j|
      begin
        p [i, j]
        raise RuntimeError if rand(10) == 0
      rescue => ex
        throw :redo_outer_loop_from_deep_loop, ex
      end
    end
  end
  p redo_flag #debug
  redo if redo_flag.class == RuntimeError
end

試しに実行させてみます。

[0, 0]
[0, 1]
#<RuntimeError: RuntimeError> # debug 出力
[0, 0]
[0, 1]
[0, 2]
#<RuntimeError: RuntimeError> # debug 出力
[0, 0]
[0, 1]
[0, 2]
3                             # debug 出力
[1, 0]
[1, 1]
#<RuntimeError: RuntimeError> # debug出力
[1, 0]
[1, 1]
[1, 2]
3                             # debug 出力
[2, 0]
#<RuntimeError: RuntimeError> # debug 出力
[2, 0]
[2, 1]
[2, 2]
3                             # debug 出力
[3, 0]
[3, 1]
[3, 2]
3                             # debug 出力
[4, 0]
#<RuntimeError: RuntimeError> # debug 出力
[4, 0]
[4, 1]
[4, 2]
3                             # debug 出力
=> 5

外側ループを5周させることが出来ていますし、内側ループのなかで RuntimeError エラーが生じたときには外側ループをやり直しさせることも出来ています。
なお debug 出力として 3 が返っているのは、内側ループが 3.times ブロックであり、その返値が 3 になるためです。

おねがい

もうちょっとキレイな書き方があれば教えてください。

0
0
1

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0