😊

Ruby の redo について

に公開

Ruby ではイテレーションを中断する処理として nextbreak が一般的に使われる事が多いです。
next はブロック内で呼び出すことでそのブロックの処理を終了させて、引き続きイテレーションが行われます。

(1..5).each {
  # 偶数であれば以降のブロック内の処理をスキップする
  next if _1.even?

  puts "#{_1}は奇数です"
}
__END__
output:
1は奇数です
3は奇数です
5は奇数です

一方 break は『イテレーション処理自体』を終了させます。

(1..5).each {
  # 4以上になればイテレーション処理を終わらせる(each 処理から抜ける)
  break if _1 >= 4

  puts "#{_1}は4未満です"
}
__END__
output:
1は4未満です
2は4未満です
3は4未満です

まあこのあたりは実際に使ったことがある人が多そうですね。

redo をするとどうなる?

一方 redo はどうなるのかというと『同じ値でもう1度ブロックが呼ばれる』という挙動になります。
どういうことかと言うとブロック内で redo を呼ぶと以下のような挙動になります。

(1..5).each {
  puts "#{_1}"

  # redo を行うと 3 を引数とするブロックがもう1度呼び出される
  # なのでこの条件分岐の場合は延々と 3 を引数とするブロックが呼び出され続ける
  redo if _1 == 3
}
__END__
output:
1
2
3
3
3
3
3
...

next が『次の値でブロックを呼び出す』のに対して redo は『前の値でブロックを呼び出す』みたいな処理になります。
なのでなにも考えないで使うと上のように無限ループしてしまう可能性があります。
いままで redo を使ったことが無いんですが使いどころってどういうところになるんですかね?

余談

るりまの redo の説明がかなりわかりやすかったです。

def iter
  # (a)
  #  :
  # (b)
  yield
  # (c)
  #  :
  # (d)
end
iter { redo  }  # -> (b) へ飛ぶ
iter { next  }  # -> (c) へ飛ぶ
iter { break }  # -> (d) へ飛ぶ

これはわかりやすい。

GitHubで編集を提案

Discussion