Qiita Teams that are logged in
You are not logged in to any team

Log in to Qiita Team
Community
OrganizationAdvent CalendarQiitadon (β)
Service
Qiita JobsQiita ZineQiita Blog
317
Help us understand the problem. What is going on with this article?
@ryo-ma

Rubyの ブロック、Proc.new、lambdaの違い

More than 1 year has passed since last update.

ブロックとProc.newはそもそも別物です。
lambdaに関してはProc.newと同じようですが若干違いがあります。

この違いを理解しておかないと、ハマる可能性があります。

ブロックとProc.new

ブロックとは

まずブロックとはdo~endまたは{~}の部分で引数のような形で渡すものです。
以下のコードでは{ puts "block" }がそれに相当します。
また、受け取り側で引数を明示的に書かなくても渡す事ができyieldでブロックを評価する事が出来ます。
おそらくlist.each{ |a| a.hoge }などで使った事があると思います。それがブロックです。

example.rb
def method_block
  yield
end
method_block { p "block" }
#実行結果
=>"block"

Proc.newとは

ブロックをオブジェクト化したものがProc.newになります。
受け取り側のメソッドで引数を明示的に書く事が出来ます。
この場合は&によりブロックがメソッドに渡ってきたときにProcオブジェクトになります。
&は引数にブロックが渡ってきたときにProcオブジェクトに変換します。
Procオブジェクトはcallメソッドによって評価する事が出来ます。

example.rb
def method_block (&block)
  block.call
end
method_block { p "block" }
#実行結果
=>"block"

ProcはクラスなのでProc.newでオブジェクトを作る事が出来ます。

example.rb
def method_proc
  proc = Proc.new { p "proc" }
  proc.call
end
method_proc
#実行結果
=>"proc"

引数を使う場合

{ |a,b,c|~ }のa,b,cが引数にあたいします。
このブロック内で使用される引数をブロックパラメータといいます。
callの引数に対応したデータを渡して上げます。

example.rb
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と違うのか。

example.rb
def method_lambda
  lambda1 = lambda{ p "lambda" }#Proc.new が返却
  lambda1.call
end
method_lambda
#実行結果
=>"lambda"

違い1つ目

lambdaはブロックパラメータの数が違うとエラーを起こしてくれます。

Proc.newで数の違う引数を渡す場合

このようにブロックパラメータが足りないの部分はnilが入ります。

example.rb
proc = Proc.new{ |a,b,c| p "#{a},#{b},#{c}" }
proc.call(2, 4)
#実行結果
=> 2,4,nil

lambdaで数の違うブロックパラメータを渡す場合

このようにエラーが発生します。
なのでlambdaのほうが厳密となります。

example.rb
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の場合

example.rb
def method_proc
  proc = Proc.new { return p "proc"}
  proc.call
  p "method_proc"
end
method_proc
#実行結果
=>"proc"

lambdaの場合

example.rb
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の違いは、ハマる可能性があるので覚えておくといいと思います。

317
Help us understand the problem. What is going on with this article?
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away

Comments

No comments
Sign up for free and join this conversation.
Sign Up
If you already have a Qiita account Login
317
Help us understand the problem. What is going on with this article?