Ruby returnとexit

オブジェクト指向の達人を目指す人のための公開ペアプロ会」(関連記事)で質問したreturnの話。

returnがいるのか?

def メソッド
  if suica.balance < price
    return nil
  end
def  ...
  ...
  ...
end

こんな感じでメソッドの最初にif文があって、returnは必要なのか?という質問。returnは戻り値を返すもので、if文は条件に合致した処理の結果を返すから、なくても一緒じゃないか?と思った。

結論から言うと自分が間違っていて、returnがないとその後の処理が行われてしまうのでreturnは必要だった。

メソッド内に一つのif文だけしかない場合などはreturnがなくても同じ結果になる。

if文の詳細:制御構造 (Ruby 2.7.0 リファレンスマニュアル)

ガード節

上のコードのように、メソッドの頭で例外処理を行うものを「ガード節」「ガード構文」などと呼ぶのだそう。

techracho.bpsinc.jp

こういう使い方は意識せずやったことがあったが、意識してやっていきたい。

returnについての勘違い

なんでもまず公式リファレンスを見る癖をつけていたはずなのに、読みもせず勘違いしていたことに気づいた。手元にある入門書にも書いてあった(少なくとも2回は読んだのに)。

return

式の値を戻り値としてメソッドの実行を終了します。式が2つ以上与えられた時には、それらを要素とする配列をメソッドの戻り値とします。式が省略された場合には nil を戻り値とします。

制御構造 (Ruby 2.7.0 リファレンスマニュアル)

メソッドではなくブロックを抜けると思ったのが間違い。

以前AtCoder競技プログラミングサイト)でreturnを使ってみたときに思い通りにならず、それ以来exitばかり使っていた。このとき調べるべきだった。

リファレンスによると、式を省略するとnilを返すので、return nilreturnと省略して書けるようだ。でもreturnの後ろにnilが隠れていることは知っておく必要がある。

exitは全プログラム終了

exit

Rubyプログラムの実行を終了します。status として整数が与えられた場合、その値を Ruby コマンドの終了ステータスとします。デフォルトの終了ステータスは 0(正常終了)です。

Kernel.#exit (Ruby 2.7.0 リファレンスマニュアル)

returnexitの違い

returnは、式の値を戻り値としてメソッドの実行を終了する。

一方exitは、その時点でプログラムを終了する。

自分が勘違いしたきっかけがどの問題だったかは覚えていないが、以前exitを使って書いたAtCoderの問題を見てみた。

B - Multiplication 2

問題文
N 個の整数 A1,...,AN が与えられます。
A1×...×AN を求めてください。
ただし、結果が 1018 を超える場合は、代わりに -1 を出力してください。

入力
入力は以下の形式で標準入力から与えられる。
N
A1 ... AN

B - Multiplication 2

N個の整数の掛け算をして、結果によって指定の値を出力するという問題。

自分なりのコードはこんな感じ。コードの解説はこちらの記事。ちなみに素直に計算してから出力すると時間オーバーになる。

n = gets
ary = gets.split.map(&:to_i)
ans = 1
if ary.include?(0)
  puts 0
  exit
end
ary.each do |a|
  ans *= a
  if ans > 10**18
    puts -1
    exit
  end
end
puts ans

exitが2回登場している。このexitreturnに置き換えたら、動くだろうか?

n = gets
ary = gets.split.map(&:to_i)
ans = 1
if ary.include?(0)
  puts 0
  return
end
ary.each do |a|
  ans *= a
  if ans > 10**18
    puts -1
    return
  end
end
puts ans

あれ、同じように動く!

リファレンスをよく見ると書いてあった。

トップレベルで return した場合はプログラムが終了します。

ということは、トップレベル(クラス構文とかの外)でのreturnexitと同じようにプログラム終了ということだ(終了ステータスなどの違いはある)。

自分がreturnを使って書こうとしたコードはどんなんだったんだろうか…うーん思い出せない。returnは戻り値を返すということを意識しすぎてreturn 〇〇〇みたいにした記憶はある。Rubyの前に少しだけPHPを勉強していたときに、returnがあってもなくても変わらないことが多いイメージがあって、よく意味も知らないままにreturnてあんまいらないなと勝手に思い込んでしまっていたのもある(反省)。

早い段階で思い込みに気づけてよかった。

質問して間違ってたことって絶対忘れないと思うから、質問してよかった。