yamarkz's blog

紫陽花

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には微妙な違いが存在する。
基本的にはどちらを使っても良いが、違いがあることだけは注意しておこう。

参考:
class Proc (Ruby 2.1.0)


以上です。