RubyのブロックとProcと、ときどきlambda
Rubyはほぼ全てがオブジェクトである。
ブロック
ブロックとは、
{}もしくはdo...endで定義するもののこと。
ブロックを定義できるのはメソッドを呼び出すときだけ。
メソッドに渡されたブロックはyieldで呼び出される。
def method(a, b) a + yield(a, b) end method(1, 2) { |x, y| (x + y) * 3 } #=> 10
hogeメソッドではyieldによってブロックが呼び出されて、aの値(1) + {}ブロック内の値(9) が行われて結果として10が返ってきます。
proc
Rubyの標準ライブラリにはProcクラスが用意されています。
Procクラスとは、
ブロックをオブジェクト化したもの
Proc.newにブロックを渡すことでオブジェクトにできる。
Proc#callで呼び出すことができる。
def method hoge = Proc.new{ p "hoge" } hoge.call end method #=> hoge
Proc#callで引数を渡すこともできます。
{|a, b, c|}の形で渡します。
def method hoge = Proc.new{ |a| p a * 2 } hoge.call(2) end method #=> 4
注意:
Rubyではほぼ全てがオブジェクトですが、ブロックはオブジェクトではありません。
ブロックをオブジェクトにするのがProcクラスです。
実はRubyでは、ブロックをオブジェクトに変換するために2つのカーネルメソッドが存在しています。
それがlambdaです。
lambdaはProcのインスタンスを生成する手段の一つという位置付けです。
Procとlambdaには微妙な違いが存在しますが、基本的には自分の好きな方を使えば良いとされています。
lambda
lambda{}でブロックオブジェクトを生成します。
呼び出しはProcと同じでcallです。
deth = lambda{ |a| a + 1 } deth.class #=> Proc dec.call(2) #=> 3
また、lambdaは「矢印ラムダ」演算子でlambdaを生成することもできます。
矢印に注目してください。
t = ->(x) { x + 1 }
これは、
t = lambda { |x| x + 1 }
と同じ意味になります。
{ |x| }
カッコ内でのxはプロックパラメータと呼ばれています。
Procとlambdaの違い
ブロックをオブジェクトにする方法を紹介しました。
lambdaで生成した場合でもオブジェクトはProcクラスでした。
ここで注意しておきたいのが、lambdaで作ったProcオブジェクトと、Proc.newで作られたものは違うということ。
この2つの違いは微妙なところです。
procがlambdaかどうかは、Proc#lambda? メソッドで確認することができます。
procとlambdaの違いはややこしいです。
returnの動きが違う。
lambdaとProcでは、returnとbreak キーワードの評価が違います。
Proc
def method a = Proc.new { return p "ikuzo" } result = a.call p "kitazo" end #=> ikuzo
lambda
def method b = lambda { return p "yaruze" } b.call p "yataze" end method #=> yaruze yataze
Procとlambdaの違いを上に示しました。
1つめの違いは、return と breakの評価が違うことです。
Procとlambdaの出力を見ると、Procではikuzoまでが評価されて、kitazoの処理が評価されていません。
しかし、lambdaの場合 yaruze の 次に yataze の処理が評価されています。
Procの場合はcallメソッド以下は評価されません。
lambdaの場合はcallメソッド以下も評価されます。
項数の違い
2つめの違いは、Procとlambdaでは引数のチェック方法が違います。
Proc
p = Proc.new { |a, b, c| p "#{a}, #{b}, #{c} } p.call(2, 3) #=> 2,3,nil
Procでは引数がたりないところはnilを返してくれます。
lambda
lambda = lambda { |a, b, c| p "#{a}, #{b}, #{c} } lambda.call(2, 3) #=> ArgumentError: wrong number of arguments (given 2, expected 3)
lambdaでは引数が足りないとerrorが返ってきます。
Procよりもlambdaの方が引数に対するチェックが厳しいです。
結論:
Rubyはほぼ全てがオブジェクトである。
do end {} などのブロックはオブジェクトではない。
ブロックをオブジェクトにするのが、Procとlambda
Procとlambdaには微妙な違いが存在する。
基本的にはどちらを使っても良いが、違いがあることだけは注意しておこう。
以上です。