要約
ブロックをコンテキスト(ローカル変数のスコープやスタックフレーム)とともにオブジェクト化した手続きオブジェクトです。
Proc は ローカル変数のスコープを導入しないことを除いて名前のない関数のように使えます。ダイナミックローカル変数は Proc ローカルの変数として使えます。
Proc がローカル変数のスコープを保持していることは以下の例で変数 var を参照できていることからわかります。
例var = 1
$foo = Proc.new { var }
var = 2
def foo
$foo.call
end
p foo # => 2
Proc オブジェクトの生成
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 #=> 1
Proc オブジェクトをブロック付きメソッド呼び出しに使う
ブロック付きメソッドに対して Proc オブジェクトを `&` を指定して渡すと呼び出しブロックのように動作します。しかし、厳密には以下の違いがあります。これらは、Proc オブジェクトが呼び出しブロックとして振舞う際の制限です。
問題なし(1..5).each { break }
LocalJumpError が発生します。
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 は引数の数が違っていてもエラーにならないb = Proc.new{|a,b,c|
p a,b,c
}
p b.call(2, 4)
#=> 2
4
nil
lambda は引数の数が違うとエラーになる
b = 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 foo': unexpected return (LocalJumpError)
以下の表は、手続きオブジェクトの実行を上の例と同じように、手続きオブジェクトが定義されたメソッドを脱出してから行った場合の結果です。
return next break
Proc.new 例外が発生する 手続きオブジェクトを抜ける 例外が発生する
proc 例外が発生する 手続きオブジェクトを抜ける 例外が発生する
lambda 手続きオブジェクトを抜ける 手続きオブジェクトを抜ける 手続きオブジェクトを抜ける
目次
- 特異メソッド
- インスタンスメソッド
特異メソッド
new { ... } -> Proc[permalink][rdoc][edit]-
ブロックをコンテキストとともにオブジェクト化して返します。
- [EXCEPTION]
ArgumentError: - ブロックを省略した呼び出しを行ったときに発生します。
pr = Proc.new {|arg| p arg } pr.call(1) # => 1Proc.new # => -e:1:in `new': tried to create Proc object without a block (ArgumentError)Proc.new は、Proc#initialize が定義されていればオブジェクトの初期化のためにこれを呼び出します。このことを除けば、Kernel.#proc と同じです。
- [EXCEPTION]
インスタンスメソッド
self << callable -> Proc[permalink][rdoc][edit]-
self と引数を合成した Proc を返します。
戻り値の Proc は可変長の引数を受け取ります。戻り値の Proc を呼び出すと、まず受け取った引数を callable に渡して呼び出し、その戻り値を self に渡して呼び出した結果を返します。
Proc#>> とは呼び出しの順序が逆になります。
- [PARAM]
callable: - Proc、Method、もしくは任意の call メソッドを持ったオブジェクト。
call を定義したオブジェクトを渡す例f = proc { |x| x * x } g = proc { |x| x + x } # (3 + 3) * (3 + 3) p (f << g).call(3) # => 36class 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 - [PARAM]
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 } p fib.(10) # => 55- [PARAM]
arg: - 手続きオブジェクトに与える引数を指定します。
- [EXCEPTION]
LocalJumpError: - Procを生成したメソッドからリターンしてしまった場合に発生します。
- [PARAM]
self >> callable -> Proc[permalink][rdoc][edit]-
self と引数を合成した Proc を返します。
戻り値の Proc は可変長の引数を受け取ります。戻り値の Proc を呼び出すと、まず受け取った引数を self に渡して呼び出し、その戻り値を callable に渡して呼び出した結果を返します。
Proc#<< とは呼び出しの順序が逆になります。
- [PARAM]
callable: - Proc、Method、もしくは任意の call メソッドを持ったオブジェクト。
call を定義したオブジェクトを渡す例f = proc { |x| x * x } g = proc { |x| x + x } # (3 * 3) + (3 * 3) p (f >> g).call(3) # => 18class 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"] - [PARAM]
arity -> Integer[permalink][rdoc][edit]-
Proc オブジェクトが受け付ける引数の数を返します。
ただし、可変長引数を受け付ける場合、負の整数
-(必要とされる引数の数 + 1)を返します。
例p lambda{ }.arity # => 0 p lambda{|| }.arity # => 0 p lambda{|x| }.arity # => 1 p lambda{|*x| }.arity # => -1 p lambda{|x, y| }.arity # => 2 p lambda{|x, *y| }.arity # => -2 p lambda{|(x, y)| }.arity # => 1 p lambda{|(x, y), z|}.arity # => 2[SEE_ALSO] Ruby用語集
binding -> Binding[permalink][rdoc][edit]-
Proc オブジェクトが保持するコンテキストを Binding オブジェクトで返します。
例def fred(param) proc {} end sample_proc = fred(99) p 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 - [PARAM]
hash -> Integer[permalink][rdoc][edit]-
self のハッシュ値を返します。
to_s -> String[permalink][rdoc][edit]inspect -> String-
self の文字列表現を返します。
可能なら self を生成したソースファイル名、行番号を含みます。
例p Proc.new { true }.to_s # => "#<Proc:0x401a880c -:3>" lambda? -> bool[permalink][rdoc][edit]-
手続きオブジェクトの引数の取扱が厳密であるならば true を返します。
引数の取扱の厳密さの意味は以下の例を参考にしてください。
例# lambda で生成した Proc オブジェクトでは true p lambda{}.lambda? # => true # proc で生成した Proc オブジェクトでは false proc{}.lambda? # => false # Proc.new で生成した Proc オブジェクトでは false p 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 p n {} # => false # &が付いた実引数によるものは、lambda?が元の Procオブジェクトから # 引き継がれる p lambda(&lambda {}).lambda? #=> true proc(&lambda {}).lambda? #=> true p Proc.new(&lambda {}).lambda? #=> true p lambda(&proc {}).lambda? #=> false proc(&proc {}).lambda? #=> false p Proc.new(&proc {}).lambda? #=> false p n(&lambda {}) #=> true p n(&proc {}) #=> false p n(&Proc.new {}) #=> false # Method#to_proc によるものは lambda?が真となる def m() end p method(:m).to_proc.lambda? #=> true # Module#define_method は特別扱いで、 # これで定義されたメソッドの引数は常に厳密に取り扱われる class C define_method(:d) {} end C.new.d(1,2) # ~> ArgumentError p C.new.method(:d).to_proc.lambda? #=> true class C define_method(:e, &proc {}) end C.new.e(1,2) # ~> ArgumentError p 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
ruby2_keywords -> proc[permalink][rdoc][edit]-
Marks the proc as passing keywords through a normal argument splat. This should only be called on procs that accept an argument splat (`*args`) but not explicit keywords or a keyword splat. It marks the proc such that if the proc is called with keyword arguments, the final hash argument is marked with a special flag such that if it is the final element of a normal argument splat to another method call, and that method call does not include explicit keywords or a keyword splat, the final element is interpreted as keywords. In other words, keywords will be passed through the proc to other methods.
This should only be used for procs that delegate keywords to another method, and only for backwards compatibility with Ruby versions before 2.7.
This method will probably be removed at some point, as it exists only for backwards compatibility. As it does not exist in Ruby versions before 2.7, check that the proc responds to this method before calling it. Also, be aware that if this method is removed, the behavior of the proc will change so that it does not pass through keywords.
module Mod foo = ->(meth, *args, &block) do send(:"do_#{meth}", *args, &block) end foo.ruby2_keywords if foo.respond_to?(:ruby2_keywords) end 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] p (eval "proc {}").source_location # => ["(eval)", 1] p 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