久しぶりの投稿です。相変わらず Rubyist です。よろしくお願いします。
なぜ内側から外側のループを redo したいの?
内側ループの中でエラーが生じたときに、外側ループを同じループでやり直しをさせたいことがあったのです。
まずは redo
の復習
redo
って何だっけ?滅多に使わないから忘れるよね。
##redo
例:
redo
文法:
>```ruby
redo
ループ条件のチェックを行なわず、現在の繰り返しをやり直します。
制御構造 (Ruby 2.4.0)
常に redo
するなんてあり得ないので if
修飾子つけたり (redo if ~~
) など、何かの条件を付けて書きますよね。
あと念のためですが
ループとは
while
until
for
- イテレータ
のいずれかを指します。
ループ構造をネストさせると redo
はどこをやり直す?
さきほどの記載にも
現在の繰り返しをやり直します。
とあったように、いまの繰り返し(ループ)をやり直します。つまりは最も内側のループをやり直します。
外側のループを redo
したいのだけど
大域脱出(catch
と throw
)を使います。
具体的には Kernel.#catch
のブロックを目的のループのすぐ内側に作ります。
catch
と throw
の復習
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
になるためです。
おねがい
もうちょっとキレイな書き方があれば教えてください。