ブロックとProc.newはそもそも別物です。
lambdaに関してはProc.newと同じようですが若干違いがあります。
この違いを理解しておかないと、ハマる可能性があります。
#ブロックとProc.new
##ブロックとは
まずブロックとはdo~endまたは{~}の部分で引数のような形で渡すものです。
以下のコードでは{ puts "block" }がそれに相当します。
また、受け取り側で引数を明示的に書かなくても渡す事ができyieldでブロックを評価する事が出来ます。
おそらくlist.each{ |a| a.hoge }などで使った事があると思います。それがブロックです。
def method_block
yield
end
method_block { p "block" }
#実行結果
=>"block"
##Proc.newとは
ブロックをオブジェクト化したものがProc.newになります。
受け取り側のメソッドで引数を明示的に書く事が出来ます。
この場合は&によりブロックがメソッドに渡ってきたときにProcオブジェクトになります。
&は引数にブロックが渡ってきたときにProcオブジェクトに変換します。
Procオブジェクトはcallメソッドによって評価する事が出来ます。
def method_block (&block)
block.call
end
method_block { p "block" }
#実行結果
=>"block"
ProcはクラスなのでProc.newでオブジェクトを作る事が出来ます。
def method_proc
proc = Proc.new { p "proc" }
proc.call
end
method_proc
#実行結果
=>"proc"
####引数を使う場合
{ |a,b,c|~ }のa,b,cが引数にあたいします。
このブロック内で使用される引数をブロックパラメータといいます。
callの引数に対応したデータを渡して上げます。
proc = Proc.new{|a| p a * 2 }
proc.call(2)
#実行結果
=>4
##違い
Rubyではほぼすべてがオブジェクトですがブロックはオブジェクトではありません。
そしてブロックをオブジェクトとしたものがProc.newです。
#Proc.newとlambda
##lambdaとは
まずlambdaの例を挙げて行きます。
lambda{~}でlambdaを使う事でき変数に代入する事も出来ます。
これはProcのインスタンスを生成して返却しているからです。
なので評価もcallメソッドで行えます。
では一体なにがProc.newと違うのか。
def method_lambda
lambda1 = lambda{ p "lambda" }#Proc.new が返却
lambda1.call
end
method_lambda
#実行結果
=>"lambda"
##違い1つ目
lambdaはブロックパラメータの数が違うとエラーを起こしてくれます。
###Proc.newで数の違う引数を渡す場合
このようにブロックパラメータが足りないの部分はnilが入ります。
proc = Proc.new{ |a,b,c| p "#{a},#{b},#{c}" }
proc.call(2, 4)
#実行結果
=> 2,4,nil
###lambdaで数の違うブロックパラメータを渡す場合
このようにエラーが発生します。
なのでlambdaのほうが厳密となります。
lambda1 = lambda{ |a,b,c| p "#{a},#{b},#{c}" }
lambda1.call(2, 4)
#実行結果
=> wrong number of arguments (2 for 3) (ArgumentError)
##違い2つ目
明示的にreturnやbreakを行った場合の挙動が違います。
###Proc.newの場合
def method_proc
proc = Proc.new { return p "proc"}
proc.call
p "method_proc"
end
method_proc
#実行結果
=>"proc"
###lambdaの場合
def method_lambda
lambda1 = lambda{ return p "lambda"}
lambda1.call
p "method_lambda"
end
method_lambda
#実行結果
=>"lambda"
"method_lambda"
違いが分かりましたよね?
Proc.newの方はcallメソッドを実行した後の処理が評価されてません。それに対してlambdaの方は最後まで評価されています。
これはProc.newの場合、returnを書くとmethod_procメソッド自体を抜けてしまうからです。lambdaの方はreturnした後、method_lambdaメソッドに戻ります。breakの場合、Proc.newでは例外が発生します。lambdaの方は同じように手続きオブジェクトを抜けます。
ただし明示的にreturnを書かなければその後も評価されます。
#まとめ
ブロックはオブジェクトではありません。
ブロックをオブジェクトとしたものがProc.newです。
Proc.newに対してlambdaの方がブロックパラメータに関しては厳密です。
returnなどの動作に関してはProcとlambdaでは異なりlambda方はメソッドに近い動きです。
特にProc.newとlambdaの違いは、ハマる可能性があるので覚えておくといいと思います。