proc { ... } -> Proc[permalink][rdoc][edit]lambda { ... } -> Proc-
与えられたブロックから手続きオブジェクト (Proc のインスタンス) を生成して返します。Proc.new に近い働きをします。
また、lambda に & 引数を渡すのは推奨されません。& 引数ではなくてブロック記法で記述する必要があります。
& 引数(リテラルでないブロック)を渡して lambda を呼び出すと、ArgumentError 「the lambda method requires a literal block」が発生します。
- [EXCEPTION]
ArgumentError: - ブロックを省略した呼び出しを行ったときに発生します。
- [EXCEPTION]
ArgumentError: - & 引数(リテラルでないブロック)を渡して呼び出したときに発生します。
def foo &block proc(&block) end it = foo{p 12} p it.call #=> 12Proc オブジェクトの生成
Proc オブジェクトを生成する手段には、主に以下の四つがあります。
* Proc.new * Kernel?.proc * Kernel?.lambda *
->(){ }(->を使った lambda の短縮記法)このうち Proc.new と Kernel?.proc は同じ性質の Proc オブジェクトを生成し、 Kernel?.lambda と
->(){ }も互いに同じ性質の Proc オブジェクトを生成しますが、前二者と後二者では、生成される Proc オブジェクトの引数の扱いや return・break の挙動などが異なります(詳細は後述)。生成された Proc オブジェクトが後二者 (lambda)に該当するかどうかは Proc#lambda? で調べることができます。このほか、メソッド呼び出しにブロックを渡した場合、そのブロックをブロック引数 (
&blockなど)で受け取ると Proc オブジェクトが生成されます。手続きを中断して値を返す
手続きオブジェクトを中断して、呼出し元(呼び出しブロックでは yield、それ以外では Proc#call) へジャンプし値を返すには next を使います。break や return ではありません。
例def foo f = Proc.new{ next 1 2 # この行に到達することはない } end p foo().call #=> 1Proc オブジェクトをブロック付きメソッド呼び出しに使う
ブロック付きメソッドに対して Proc オブジェクトを `&` を指定して渡すと呼び出しブロックのように動作します。しかし、厳密には以下の違いがあります。これらは、Proc オブジェクトが呼び出しブロックとして振舞う際の制限です。
問題なし
LocalJumpError が発生します。(1..5).each { break }pr = Proc.new { break } (1..5).each(&pr)lambda と proc と Proc.new とイテレータの違い
Kernel?.lambda と Proc.new はどちらも Proc クラスのインスタンス(手続きオブジェクト)を生成しますが、生成された手続きオブジェクトはいくつかの場面で挙動が異なります。 lambda の生成する手続きオブジェクトのほうがよりメソッドに近い働きをするように設計されています。
Kernel?.proc は Proc.new と同じになります。引数に & を付けることで手続きオブジェクト化したブロックは、Proc.new で生成されたそれと同じように振る舞います。
引数の扱い
lambda のほうがより厳密です。引数の数が違っていると(メソッドのように)エラーになります。 Proc.new は引数を多重代入に近い扱い方をします。
Proc.new は引数の数が違っていてもエラーにならない
lambda は引数の数が違うとエラーになるb = Proc.new{|a,b,c| p a,b,c } p b.call(2, 4) #=> 2 4 nilb = lambda{|a,b,c| p a,b,c } p b.call(2, 4) # => wrong number of arguments (given 2, expected 3)メソッド呼び出し/ブロックパラメータの挙動 も参照してください。
ジャンプ構文の挙動の違い
return と break は、lambda と Proc.new では挙動が異なります。例えば return を行った場合、lambda では手続きオブジェクト自身を抜けますが、 Proc.new では手続きオブジェクトを囲むメソッドを抜けます。
例def test_proc f = Proc.new { return :from_proc } f.call return :from_method end def test_lambda f = lambda { return :from_lambda } f.call return :from_method end def test_block tap { return :from_block } return :from_method end p test_proc() #=> :from_proc p test_lambda() #=> :from_method p test_block() #=> :from_block以下の表は、手続きオブジェクトの実行を上の例と同じように、手続きオブジェクトが定義されたのと同じメソッド内で行った場合の結果です。
return next break Proc.new メソッドを抜ける 手続きオブジェクトを抜ける 例外が発生する proc メソッドを抜ける 手続きオブジェクトを抜ける 例外が発生する lambda 手続きオブジェクトを抜ける 手続きオブジェクトを抜ける 手続きオブジェクトを抜ける イテレータ メソッドを抜ける 手続きオブジェクトを抜ける 手続きオブジェクトを抜けるorphan な手続きオブジェクトの挙動
Proc(lambda を除く)を生成したメソッド呼び出しが終了した後で、その手続きオブジェクトに対して return を実行すると、戻る先のメソッド呼び出しがすでに存在しないため、例外 LocalJumpError が発生します。
break の場合は条件がやや異なります。その手続きオブジェクトをブロックとして受け取ったメソッド呼び出しがすでに終了している場合に加えて、
Proc.new { break }.callのようにブロックとしてメソッドに渡されていない手続きオブジェクトで break を実行した場合にも、脱出先となるメソッド呼び出しが存在しないため例外 LocalJumpError が発生します。ここでいう「作成元が終了している」とは、生成元のコード上の位置(レキシカルスコープ)が失われるという意味ではありません。同じメソッドを複数回呼び出した場合、それぞれの呼び出しで生成された手続きオブジェクトは、呼び出しごとに独立してこの状態になります。
ただし、上でも説明した通り lambda で生成した手続きオブジェクトはメソッドと同じように振る舞うことを意図されているため、例外 LocalJumpError は発生しません。
例def foo Proc.new { return } end foo.call # => in 'block in Object#foo': unexpected return (LocalJumpError)以下の表は、手続きオブジェクトの実行を上の例と同じように、手続きオブジェクトが定義されたメソッドを脱出してから行った場合の結果です。
return next break Proc.new 例外が発生する 手続きオブジェクトを抜ける 例外が発生する proc 例外が発生する 手続きオブジェクトを抜ける 例外が発生する lambda 手続きオブジェクトを抜ける 手続きオブジェクトを抜ける 手続きオブジェクトを抜ける - [EXCEPTION]