proc とメソッドまたはラムダの重要な違いは、return ステートメントを処理する方法です。メソッドが別のメソッド内で定義されている場合、内部メソッドの return ステートメントは内部メソッド自体からのみ終了し、外部メソッドは実行を継続します。同じことが、ラムダ内のラムダ、メソッド内のラムダ、またはラムダ内のメソッドの定義にも当てはまります。ただし、proc がメソッド内で定義されている場合、return ステートメントは proc および外側の (囲んでいる) メソッドから終了します。例:
def meditate
puts "Adjusting posture…"
p = Proc.new { puts "Ringing bell…"; return }
p.call
puts "Sitting still…" # This is not executed
end
meditate
Output:
Adjusting posture…
Ringing bell…
proc 内の return ステートメントが proc と外側のメソッドの両方から終了したため、メソッドの最後の行が実行されていないことに注意してください。
外側のメソッドなしで proc を定義し、return ステートメントを使用すると、LocalJumpError がスローされます。
p = Proc.new { puts "Ringing bell…"; return }
p.call
Output:
Ringing bell…
LocalJumpError: unexpected return
これは、return ステートメントが proc 内で到達したときに、それが呼び出されたコンテキストから戻るのではなく、それ (proc) が定義されたスコープから戻るために発生します。次の例では、proc が定義された最上位の環境から返そうとしているため、LocalJumpError が発生します。
def meditate p
puts "Adjusting posture…"
p.call
puts "Sitting still…" # This is not executed
end
p = Proc.new { puts "Ringing bell…"; return }
meditate p
Output:
Adjusting posture…
Ringing bell…
LocalJumpError: unexpected return
通常、proc 内で return ステートメントを使用することはお勧めできません。通常、proc はメソッド間で渡され、proc が定義されたメソッドが既に返されている場合は、例外がスローされます。以下の例では、return ステートメントを削除するだけです。ただし、実際に何かを返す必要がある場合があります。後者では、proc の代わりに lambda を使用するのがおそらく最善です。後で、ラムダが return ステートメントを別の方法で、よりメソッドのように処理することを確認します。
以下は、return ステートメントを含む別のシナリオです。
def zafu_factory
# This method will return the following proc implicitly
Proc.new { puts "Round black zafu"; return }
end
def meditate
puts "Adjusting posture…"
p = zafu_factory
p.call
puts "Sitting still…" # This is not executed
end
meditate
Output:
Adjusting posture…
Round black zafu
LocalJumpError: unexpected return
今何があったの?zafu_factory メソッドが作成し、暗黙的に proc を返しました。その後、proc は medicate メソッドによって呼び出され、proc 内の return ステートメントに到達すると、それが定義されたコンテキスト (zafu_factory メソッド) から戻ろうとしました。ただし、zafu_factory は既に proc を返しているため、メソッドは呼び出されるたびに 1 回しか返せません。つまり、プロシージャが呼び出されて 2 回目に返そうとしたときに、zafu_factory メソッドが既に返っていたため、例外がスローされました。
Procs と Lambdas: Closures in Rubyに関するこのブログ投稿で詳細を参照してください。