ブロックをコンテキスト(ローカル変数のスコープやスタックフレーム)とともにオブジェクト化した手続きオブジェクトです。
Proc は ローカル変数のスコープを導入しないことを除いて名前のない関数のように使えます。ダイナミックローカル変数は Proc ローカルの変数として使えます。
Proc がローカル変数のスコープを保持していることは以下の例で変数 var を参照できていることからわかります。
var = 1
$foo = Proc.new { var }
var = 2
def foo
$foo.call
end
p foo # => 2
手続きオブジェクトを中断して、呼出し元(呼び出しブロックでは yield、それ以外では Proc#call) へジャンプし値を返すには next を使います。break や return ではありません。
例:
def foo
f = Proc.new{
next 1
2
}
end
p foo().call #=> 1
ブロック付きメソッドに対して Proc オブジェクトを `&' を指定して渡すと呼び出しブロックのように動作します。しかし、厳密には以下の違いがあります。これらは、Proc オブジェクトが呼び出しブロックとして振舞う際の制限です。
# 問題なし
(1..5).each { break }
# LocalJumpError が発生します。
pr = Proc.new { break }
(1..5).each(&pr)
Kernel.#lambda と Proc.new はどちらも Proc クラスのインスタンス(手続きオブジェクト)を生成しますが、生成された手続きオブジェクトはいくつかの場面で挙動が異なります。 lambda の生成する手続きオブジェクトのほうがよりメソッドに近い働きをするように設計されています。
Kernel.#proc は Proc.new と同じになります。引数に & を付けることで手続きオブジェクト化したブロックは、Proc.new で生成されたそれと同じにように振る舞います。
lambda のほうがより厳密です。引数の数が違っていると(メソッドのように)エラーになります。 Proc.new は引数を多重代入に近い扱い方をします。
例:
b1 = Proc.new{|a,b,c|
p a,b,c
}
b1.call(2, 4)
#=> 2
4
nil
b2 = lambda{|a,b,c|
p a,b,c
}
b2.call(2, 4)
#=> wrong number of arguments (given 2, expected 3)
メソッド呼び出し(super・ブロック付き・yield)/ブロックパラメータの挙動 も参照してください。
return と break は、lambda と Proc.new では挙動が異なります。例えば return を行った場合、lambda では手続きオブジェクト自身を抜けますが、 Proc.new では手続きオブジェクトを囲むメソッドを抜けます。
例:
def foo
f = Proc.new { return :foo }
f.call
return
end
def bar
f = lambda { return :bar }
f.call
return
end
def h
yield
end
def hoge
h{ return :hoge }
nil
end
p foo() #=> :foo
p bar() #=> nil
p hoge() #=> :hoge
以下の表は、手続きオブジェクトの実行を上の例と同じように、手続きオブジェクトが定義されたのと同じメソッド内で行った場合の結果です。
return next break
Proc.new メソッドを抜ける 手続きオブジェクトを抜ける 例外が発生する
proc メソッドを抜ける 手続きオブジェクトを抜ける 例外が発生する
lambda 手続きオブジェクトを抜ける 手続きオブジェクトを抜ける 手続きオブジェクトを抜ける
イテレータ メソッドを抜ける 手続きオブジェクトを抜ける メソッドを抜ける
Proc を生成したメソッドから脱出した後、手続きオブジェクトからの return, break は例外 LocalJumpError を発生させます。ただし、上でも説明した通り lambda で生成した手続きオブジェクトはメソッドと同じように振る舞うことを意図されているため、例外 LocalJumpError は発生しません。
例:
def foo
Proc.new { return }
end
foo.call
# => in `call': return from proc-closure (LocalJumpError)
以下の表は、手続きオブジェクトの実行を上の例と同じように、手続きオブジェクトが定義されたメソッドを脱出してから行った場合の結果です。
return next break
Proc.new 例外が発生する 手続きオブジェクトを抜ける 例外が発生する
proc 例外が発生する 手続きオブジェクトを抜ける 例外が発生する
lambda 手続きオブジェクトを抜ける 手続きオブジェクトを抜ける 手続きオブジェクトを抜ける
new -> Proc[permalink][rdoc]new { ... } -> Procブロックをコンテキストとともにオブジェクト化して返します。
ブロックを指定しなければ、このメソッドを呼び出したメソッドがブロックを伴うときに、それを Proc オブジェクトとして生成して返します。
def foo
pr = Proc.new
pr.call(1)
end
foo {|arg| p arg }
# => 1
これは以下と同じです。
def foo
yield(1)
end
foo {|arg| p arg }
# => 1
呼び出し元のメソッドがブロックを伴わなければ、例外 ArgumentError が発生します。
def foo
Proc.new
end
foo
# => -:2:in `new': tried to create Proc object without a block (ArgumentError)
from -:2:in `foo'
from -:4:in `<main>'
Proc.new は、Proc#initialize が定義されていればオブジェクトの初期化のためにこれを呼び出します。このことを除けば、Kernel.#proc と同じです。
self[*arg] -> ()[permalink][rdoc]call(*arg) -> ()self === *arg -> ()yield(*arg) -> ()手続きオブジェクトを実行してその結果を返します。
引数の渡され方はオブジェクトの生成方法によって異なります。詳しくは Proc#lambda? を参照してください。
「===」は when の所に手続きを渡せるようにするためのものです。
def sign(n)
case n
when lambda{|n| n > 0} then 1
when lambda{|n| n < 0} then -1
else 0
end
end
p sign(-4) #=> -1
p sign(0) #=> 0
p sign(7) #=> 1
また、以下のような syntactic sugar もあります。
fib = lambda{|n|
case n
when 0 then 0
when 1 then 1
else
fib.(n - 2) + fib.(n - 1)
end
}
fib.(10) # => 55
arity -> Integer[permalink][rdoc]Proc オブジェクトが受け付ける引数の数を返します。
ただし、可変長引数を受け付ける場合、負の整数
-(必要とされる引数の数 + 1)
を返します。
例:
lambda{ }.arity # => 0
lambda{|| }.arity # => 0
lambda{|x| }.arity # => 1
lambda{|*x| }.arity # => -1
lambda{|x, y| }.arity # => 2
lambda{|x, *y| }.arity # => -2
lambda{|(x, y)| }.arity # => 1
lambda{|(x, y), z|}.arity # => 2
binding -> Binding[permalink][rdoc]Proc オブジェクトが保持するコンテキストを Binding オブジェクトで返します。
例:
def fred(param)
proc {}
end
sample_proc = fred(99)
eval("param", sample_proc.binding) # => 99
curry -> Proc[permalink][rdoc]curry(arity) -> ProcProcをカリー化します
カリー化したProcはいくつかの引数をとります。十分な数の引数が与えられると、元のProcに引数を渡して実行し、結果を返します。引数の個数が足りないときは、部分適用したカリー化Procを返します。
b = proc {|x, y, z| (x||0) + (y||0) + (z||0) }
p b.curry[1][2][3] #=> 6
p b.curry[1, 2][3, 4] #=> 6
p b.curry(5)[1][2][3][4][5] #=> 6
p b.curry(5)[1, 2][3, 4][5] #=> 6
p b.curry(1)[1] #=> 1
b = proc {|x, y, z, *w| (x||0) + (y||0) + (z||0) + w.inject(0, &:+) }
p b.curry[1][2][3] #=> 6
p b.curry[1, 2][3, 4] #=> 10
p b.curry(5)[1][2][3][4][5] #=> 15
p b.curry(5)[1, 2][3, 4][5] #=> 15
p b.curry(1)[1] #=> 1
b = lambda {|x, y, z| (x||0) + (y||0) + (z||0) }
p b.curry[1][2][3] #=> 6
p b.curry[1, 2][3, 4] #=> wrong number of arguments (given 4, expected 3)
p b.curry(5) #=> wrong number of arguments (given 5, expected 3)
p b.curry(1) #=> wrong number of arguments (given 1, expected 3)
b = lambda {|x, y, z, *w| (x||0) + (y||0) + (z||0) + w.inject(0, &:+) }
p b.curry[1][2][3] #=> 6
p b.curry[1, 2][3, 4] #=> 10
p b.curry(5)[1][2][3][4][5] #=> 15
p b.curry(5)[1, 2][3, 4][5] #=> 15
p b.curry(1) #=> wrong number of arguments (given 1, expected 3+)
b = proc { :foo }
p b.curry[] #=> :foo
hash -> Integer[permalink][rdoc]self のハッシュ値を返します。
to_s -> String[permalink][rdoc]inspect -> Stringself の文字列表現を返します。
可能なら self を生成したソースファイル名、行番号を含みます。
p Proc.new {
true
}.to_s
=> "#<Proc:0x0x401a880c@-:3>"
lambda? -> bool[permalink][rdoc]手続きオブジェクトの引数の取扱が厳密であるならば true を返します。
引数の取扱の厳密さの意味は以下の例を参考にしてください。例:
# lambda で生成した Proc オブジェクトでは true
lambda{}.lambda? # => true
# proc で生成した Proc オブジェクトでは false
proc{}.lambda? # => false
# Proc.new で生成した Proc オブジェクトでは false
Proc.new{}.lambda? # => false
# 以下、lambda?が偽である場合
# 余分な引数を無視する
proc{|a,b| [a,b]}.call(1,2,3) # => [1,2]
# 足りない引数には nil が渡される
proc{|a,b| [a,b]}.call(1) # => [1, nil]
# 配列1つだと展開される
proc{|a,b| [a,b]}.call([1,2]) # => [1,2]
# lambdaの場合これらはすべて ArgumentError となる
# &が付いた仮引数で生成される Proc は lambda? が偽となる
def n(&b) b.lambda? end
n {} # => false
# &が付いた実引数によるものは、lambda?が元の Procオブジェクトから
# 引き継がれる
lambda(&lambda {}).lambda? #=> true
proc(&lambda {}).lambda? #=> true
Proc.new(&lambda {}).lambda? #=> true
lambda(&proc {}).lambda? #=> false
proc(&proc {}).lambda? #=> false
Proc.new(&proc {}).lambda? #=> false
n(&lambda {}) #=> true
n(&proc {}) #=> false
n(&Proc.new {}) #=> false
# Method#to_proc によるものは lambda?が真となる
def m() end
method(:m).to_proc.lambda? #=> true
# Module#define_method は特別扱いで、
# これで定義されたメソッドの引数は常に厳密に取り扱われる
class C
define_method(:d) {}
end
C.new.d(1,2) #=> ArgumentError
C.new.method(:d).to_proc.lambda? #=> true
class C
define_method(:e, &proc {})
end
C.new.e(1,2) #=> ArgumentError
C.new.method(:e).to_proc.lambda? #=> true
parameters -> [object][permalink][rdoc]Proc オブジェクトの引数の情報を返します。
Proc オブジェクトが引数を取らなければ空の配列を返します。引数を取る場合は、配列の配列を返し、各配列の要素は引数の種類に対応した以下のような Symbol と、引数名を表す Symbol の 2 要素です。
必須の引数
デフォルト値が指定されたオプショナルな引数
* で指定された残りすべての引数
必須のキーワード引数
デフォルト値が指定されたオプショナルなキーワード引数
** で指定された残りのキーワード引数
& で指定されたブロック引数
例:
prc = lambda{|x, y=42, *other, k_x:, k_y: 42, **k_other, &b|}
prc.parameters #=> [[:req, :x], [:opt, :y], [:rest, :other], [:keyreq, :k_x], [:key, :k_y], [:keyrest, :k_other], [:block, :b]]
[SEE_ALSO] Method#parameters, UnboundMethod#parameters
source_location -> [String, Integer] | nil[permalink][rdoc]ソースコードのファイル名と行番号を配列で返します。
その手続オブジェクトが ruby で定義されていない(つまりネイティブである)場合は nil を返します。
例:
# /path/to/target.rb を実行
proc {}.source_location # => ["/path/to/target.rb", 1]
proc {}.source_location # => ["/path/to/target.rb", 2]
(eval "proc {}").source_location # => ["(eval)", 1]
method(:p).to_proc.source_location # => nil
[SEE_ALSO] Method#source_location
to_proc -> self[permalink][rdoc]self を返します。
例:
pr = proc {}
p pr == pr.to_proc # => true