🤷‍♂️

RubyのProcとlambdaの違いについて2つ答えられますか?

2024/03/30に公開

Rubyでブロックをオブジェクトとして扱いたいときにProcとlambdaが利用できます。

しかし、両者の違いについて毎回忘れてしまうので、自分用に簡潔にまとめることにしました。
私と同様に毎回忘れてしまう方の参考になれば幸いです。

引数の違い

Procとlambdaの1つ目の違いは、引数のチェック方法です。

  • lambda
    • 引数の数が一致しない場合がエラーを発生させます。
  • Proc
    • 引数の数が一致しなくてもエラーが発生しません。
    • 足りない場合はnilで置き換えられ、余分な場合は無視されます。

例えばProcとlambdaの項数が2つあったとします。

p = Proc.new { |a, b| puts "#{a}, #{b}" }
l = lambda { |a, b| puts "#{a}, #{b}" }

この呼び出し可能オブジェクトの引数を1つ増やしたり、減らしたりしてみましょう。

p.call(1, 2, 3) #=> 1, 2
p.call(1) #=> 1,

l.call(1, 2, 3) #=> wrong number of arguments (given 3, expected 2) (ArgumentError)
l.call(1) # => wrong number of arguments (given 1, expected 2) (ArgumentError)

このように引数の数が多すぎるとProcは多い部分を切り落とし、引数の数が少ないと足りない部分にnilを入れます。
一方でlambdaではエラーが発生します。

returnの扱い

2つ目の違いはreturnキーワードの意味が違います。

  • lambda
    • lambdaの中でreturnを使うとlambda自身から戻ります。
  • Proc
    • procの中でreturnを使うとprocを含むスコープ(外側のメソッドなど)から戻ろうとします。

言葉だとわかりにくいので実際にコードを見てみましょう。

# returnの挙動の違い
def test_lambda
  l = lambda { return }
  l.call
  puts "Lambda finished"
end

def test_proc
  p = Proc.new { return }
  p.call
  puts "Proc finished" # この行は実行されない
end

ProcではProcから戻るのではなくProcが定義されたスコープから戻ることが確認できます。
この挙動を知らないと以下のようなバグを引き起こすので要注意です。

def inc(callable_object)
    callable_object.call + 1
end

p = Proc.new { return 10 }
inc(p) #=> 11を期待してるのに何も返さない?

結局どっちを使えば良いの?

Procは柔軟な引数の扱いを許容したいときや、呼び出し元のコンテキストに即してreturnしたい場合に適しています。

一方でlambdaは厳密な引数のチェックと、局所的なreturnの挙動を必要とする場合に適しています。メソッドに近い振る舞いを期待する場合に便利です。

Discussion