要約
ブロックをコンテキスト(ローカル変数のスコープやスタックフレーム)とともにオブジェクト化した手続きオブジェクトです。
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 オブジェクトを `&' を指定して渡すと呼び出しブロックのように動作します。しかし、厳密には以下の違いがあります。これらは、Proc オブジェクトが呼び出しブロックとして振舞う際の制限です。
(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 は引数を多重代入に近い扱い方をします。
b = Proc.new{|a,b,c|
p a,b,c
}
b.call(2, 4)
#=> 2
4
nil
b = lambda{|a,b,c|
p a,b,c
}
b.call(2, 4)
# => wrong number of arguments (given 2, expected 3)
メソッド呼び出し(super・ブロック付き・yield)/ブロックパラメータの挙動 も参照してください。
ジャンプ構文の挙動の違い
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 を生成したメソッドから脱出した後、手続きオブジェクトからの 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][edit]new { ... } -> Proc
-
ブロックをコンテキストとともにオブジェクト化して返します。
ブロックを指定しない場合、Ruby 2.7 では $VERBOSE = true のときには警告メッセージ「warning: Capturing the given block using Proc.new is deprecated; use `&block` instead」が出力され、Ruby 3.0 では ArgumentError (tried to create Proc object without a block) が発生します。
ブロックを指定しなければ、このメソッドを呼び出したメソッドがブロックを伴うときに、それを Proc オブジェクトとして生成して返します。
ただし、ブロックを指定しない呼び出しは推奨されていません。呼び出し元のメソッドで指定されたブロックを得たい場合は明示的に & 引数でうけるべきです。
- [EXCEPTION] ArgumentError:
- スタック上にブロックがないのにブロックを省略した呼び出しを行ったときに発生します。
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 << callable -> Proc
[permalink][rdoc][edit]-
self と引数を合成した Proc を返します。
戻り値の Proc は可変長の引数を受け取ります。戻り値の Proc を呼び出すと、まず受け取った引数を callable に渡して呼び出し、その戻り値を self に渡して呼び出した結果を返します。
Proc#>> とは呼び出しの順序が逆になります。
- [PARAM] callable:
- Proc、Method、もしくは任意の call メソッドを持ったオブジェクト。
f = proc { |x| x * x } g = proc { |x| x + x } # (3 + 3) * (3 + 3) p (f << g).call(3) # => 36
class WordScanner def self.call(str) str.scan(/\w+/) end end File.write('testfile', <<~TEXT) Hello, World! Hello, Ruby! TEXT pipeline = proc { |data| puts "word count: #{data.size}" } << WordScanner << File.method(:read) pipeline.call('testfile') # => word count: 4
self[*arg] -> ()
[permalink][rdoc][edit]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
- [PARAM] arg:
- 手続きオブジェクトに与える引数を指定します。
- [EXCEPTION] LocalJumpError:
- Procを生成したメソッドからリターンしてしまった場合に発生します。
self >> callable -> Proc
[permalink][rdoc][edit]-
self と引数を合成した Proc を返します。
戻り値の Proc は可変長の引数を受け取ります。戻り値の Proc を呼び出すと、まず受け取った引数を self に渡して呼び出し、その戻り値を callable に渡して呼び出した結果を返します。
Proc#<< とは呼び出しの順序が逆になります。
- [PARAM] callable:
- Proc、Method、もしくは任意の call メソッドを持ったオブジェクト。
f = proc { |x| x * x } g = proc { |x| x + x } # (3 * 3) + (3 * 3) p (f >> g).call(3) # => 18
class WordScanner def self.call(str) str.scan(/\w+/) end end File.write('testfile', <<~TEXT) Hello, World! Hello, Ruby! TEXT pipeline = proc { |fname| File.read(fname) } >> WordScanner >> method(:p) pipeline.call('testfile') # => ["Hello", "World", "Hello", "Ruby"]
arity -> Integer
[permalink][rdoc][edit]-
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][edit]-
Proc オブジェクトが保持するコンテキストを Binding オブジェクトで返します。
def fred(param) proc {} end sample_proc = fred(99) eval("param", sample_proc.binding) # => 99
curry -> Proc
[permalink][rdoc][edit]curry(arity) -> Proc
-
Procをカリー化します
カリー化したProcはいくつかの引数をとります。十分な数の引数が与えられると、元のProcに引数を渡して実行し、結果を返します。引数の個数が足りないときは、部分適用したカリー化Procを返します。
- [PARAM] arity:
- 引数の個数を指定します
- [RETURN]
- カリー化した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.sum } 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.sum } 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][edit]-
self のハッシュ値を返します。
Proc オブジェクトの引数の情報を返します。
Proc オブジェクトが引数を取らなければ空の配列を返します。引数を取る場合は、配列の配列を返し、各配列の要素は引数の種類に対応した以下のような Symbol と、引数名を表す Symbol の 2 要素です。
prc = proc{|x, y=42, *other|} p prc.parameters # => [[:opt, :x], [:opt, :y], [:rest, :other]] prc = lambda{|x, y=42, *other|} p prc.parameters # => [[:req, :x], [:opt, :y], [:rest, :other]] prc = proc{|x, y=42, *other|} p prc.parameters(lambda: true) # => [[:req, :x], [:opt, :y], [:rest, :other]] prc = lambda{|x, y=42, *other|} p prc.parameters(lambda: false) # => [[:opt, :x], [:opt, :y], [:rest, :other]]
to_s -> String
[permalink][rdoc][edit]inspect -> String
-
self の文字列表現を返します。
可能なら self を生成したソースファイル名、行番号を含みます。
p Proc.new { true }.to_s # => "#<Proc:0x0x401a880c@-:3>"
lambda? -> bool
[permalink][rdoc][edit]-
手続きオブジェクトの引数の取扱が厳密であるならば 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][edit]-
Proc オブジェクトの引数の情報を返します。
Proc オブジェクトが引数を取らなければ空の配列を返します。引数を取る場合は、配列の配列を返し、各配列の要素は引数の種類に対応した以下のような Symbol と、引数名を表す Symbol の 2 要素です。
- :req
-
必須の引数
- :opt
-
デフォルト値が指定されたオプショナルな引数
- :rest
-
* で指定された残りすべての引数
- :keyreq
-
必須のキーワード引数
- :key
-
デフォルト値が指定されたオプショナルなキーワード引数
- :keyrest
-
** で指定された残りのキーワード引数
- :block
-
& で指定されたブロック引数
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][edit]-
ソースコードのファイル名と行番号を配列で返します。
その手続オブジェクトが 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][edit]-
self を返します。
pr = proc {} p pr == pr.to_proc # => true