Rubyの「Procオブジェクト」について学んだことをまとめてみました。本記事よりもより正確かつ詳細な内容が必要な場合は、巻末に記載の参考文献やリファレンスマニュアルなどをご参照ください。
Procオブジェクトとは
Procオブジェクトとはブロックをオブジェクト化したProcクラスのインスタンスです。
Procオブジェクトの作り方
Procオブジェクトの作り方は複数あります。
# 作り方その1 (Proc.new)
proc_sample1 = Proc.new "ブロック"
# 作り方その2 (procメソッド)
proc_sample2 = proc "ブロック"
# 作り方その3 ("->")
proc_sample3 = ->"引数のリスト" "引数を使って実行する処理の内容"
# 作り方その4 (lambdaメソッド)
proc_sample4 = lambda "ブロック"
Procオブジェクトの使い方
Procオブジェクトを実行
Procオブジェクトはブロックのオブジェクトなので、そのままでは実行されません。Procオブジェクトを実行する方法は複数あります。
# Procオブジェクトを生成
add_proc = ->(a, b) { a + b }
# 実行方法その1 (callメソッド)
add_proc.call(1, 2) #=> 3
# 実行方法その2 (yieldメソッド)
add_proc.yield(1, 2) #=> 3
# 実行方法その3 (".()")
add_proc.(1, 2) #=> 3
# 実行方法その4 ("[]")
add_proc[1, 2] #=> 3
Procオブジェクトをブロックの代わりとして渡す
引数にブロックを受け取るメソッドに対して、ブロックの代わりにProcオブジェクトを渡すことができます。
# 引数にブロックを受け取るメソッドを定義
def greeting(&block)
puts 'おはよう'
text = block.call('こんにちは')
puts text
puts 'こんばんは'
end
# Procオブジェクトを生成
shout_proc = ->(text) { text + '!' * 3 }
# Procオブジェクトをブロックの代わりに渡す
greeting(&shout_proc)
#=> おはよう
# こんにちは!!!
# こんばんは
Procオブジェクトを普通の引数として渡す
Procオブジェクトを普通の引数としてメソッドに渡すこともできます。
# 普通の引数を受け取るメソッドを定義
def greeting(arrange_proc)
puts 'おはよう'
text = arrange_proc.call('こんにちは')
puts text
puts 'こんばんは'
end
# Procオブジェクトを生成
shout_proc = Proc.new { |text| text + '!' * 3 }
# Procオブジェクトを普通の引数として渡す
greeting(shout_proc)
#=> おはよう
# こんにちは!!!
# こんばんは
Proc.newとlambdaの違い
Procオブジェクトの作り方その1およびその2で生成したProcオブジェクト(ここでは"Proc.new"と呼びます)と、その3およびその4で生成したProcオブジェクト(ここでは"lambda"と呼びます)は、挙動が少し異なります。
違いその1 〜引数の扱い〜
lambdaはProc.newよりも引数のチェックが厳密です。
# Proc.newは 「想定している引数の数」 と 「実際に渡された引数の数」 が不一致でも動作する
add_proc = Proc.new { |a, b| a.to_i + b.to_i }
add_proc.call(1) #=> 1 (不足している引数は nil として扱われる)
add_proc.call(1, 3, 5) #=> 4 (3つ目の引数は無視される)
# lambdaは 「想定している引数の数」 と 「実際に渡された引数の数」 が不一致の場合はエラーとなる
add_proc = ->(a, b) { a.to_i + b.to_i }
add_proc.call(1) #=> ArgumentError: wrong number of arguments (given 1, expected 2)
add_proc.call(1, 3, 5) #=> ArgumentError: wrong number of arguments (given 3, expected 2)
違いその2 〜return/breakの挙動〜
Proc.newやlambdaの中でreturn/breakを使った場合、以下のような挙動の違いがあります。
return | break | |
---|---|---|
Proc.new | メソッドを抜ける | 例外が発生する |
lambda | lambda内の処理から抜ける | lambda内の処理から抜ける |
厳密にはもう少し違いはあるのですが、詳しく知りたい方は以下を参照ください。
・returnやbreakを使ったときのProc.newとラムダの挙動の違い
・Ruby リファレンスマニュアル
上記挙動の違いはややこしいので、Proc.newやlambdaの中ではできるだけreturn/breakを使わないようにするのが賢明のようです。