0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

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

Last updated at Posted at 2017-01-28

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

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

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

まずは redo の復習

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

##redo
例:

redo

文法:

>```ruby
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)](https://docs.ruby-lang.org/ja/latest/method/Kernel/m/catch.html)

<br>
> `throw` は同じ tag を指定した `catch` のブロックの終わりまでジャンプします。
[module function `Kernel.#throw` (Ruby 2.4.0)](https://docs.ruby-lang.org/ja/latest/method/Kernel/m/throw.html)

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

## では実際に外側ループの `redo` をやってみよう
外側ループを5周させますが、内側ループ(3周)の間にエラーが生じたら外側ループをやり直しさせます。

```ruby
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

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?