b2args(1,&pr)というように、実引数のProcオブジェクトに&を付けてb2argsに渡している理由は、b2argsの仮引数に&をつけているため。Procオブジェクトをブロックに変換したものが、b2argsに渡した時点で再度Procオブジェクトに変換される。- yieldは、仮引数の最後に明示的に渡されたブロックか、あるいは仮引数になく暗黙的に渡されたブロックを受け取って実行することができる。
&で受けるProcオブジェクトや、lambdaオブジェクト、あるいは**で受けるキーワード引数は、仮引数の最後にしか書けない。b2argslmにはlambdaオブジェクトをそのまま渡していて、lambdaオブジェクトのclassがProcであることが分かる。- Proc.newで作るProcオブジェクトとlambdaオブジェクトの違いは、ブロックに対して呼び出し時の引数の数が少ない場合に、Procオブジェクトではnilが入れられて実行が継続されるが、lambdaオブジェクトではArgumentErrorとなる。*1
block_given?を使えば、ブロックが渡された場合にTrueを返すので、ブロックが渡されたか渡されていないかで分岐した処理が書ける。
プログラム
$ ruby --version ruby 2.2.2p95 (2015-04-13 revision 50295) [x86_64-darwin14] $ cat proc.rb def b2args(i,&b) puts i.class puts b puts b.class puts "-- block.call --" b.call puts "-- block.call(i) --" b.call(i) puts "-- yield --" yield puts "-- yield(i) --" yield(i) puts "// the end of method" end def b2argslm(i,l) puts i.class puts l puts l.class puts "-- lambda.call(i) --" l.call(i) puts "// the end of method" end def b1arg(a) if block_given? puts a.class puts "-- yield --" yield puts "-- yield(a) --" yield(a) else puts "No block given" end end def b3argsany(a,*b,**c) puts a.class puts b.class puts c.class puts "-- yield(a,b,c) --" yield(a,b,c) end pr=Proc.new{|i|puts "pr called with #{i}"} lm=lambda{|i|puts "lm called with #{i}"} puts "== pr.call ==" pr.call puts "== pr.call(1) ==" pr.call(1) puts "" #puts "== lm.call ==" #lm.call # => ArgumentError puts "== lm.call(1) ==" lm.call(1) puts "" puts "== b2args(1,&pr) ==" b2args(1,&pr) puts "" puts "== b2argslm(1,lm) ==" b2argslm(1,lm) puts "" puts "== b1arg(2){|a|...} ==" b1arg(2){|a|puts "b1arg called with #{a}"} puts "" puts "== b1arg(0) ==" b1arg(0) puts "" puts "== b3argsany([1,2,3]){|a,b,c|...} ==" b3argsany([1,2,3]){|a,b,c|puts "b3argsany called with #{a}, #{b} and #{c}"} puts "" puts "== b3argsany(*[1,2,3],{x:10}){|a,b,c|...} with multi-assignment ==" b3argsany(*[1,2,3],{x:10}){|a,b,c|puts "b3argsany called with #{a}, #{b} and #{c}"} puts "" puts "== b3argsany(0,{x:10},*[1,2,3]){|a,b,c|...} with multi-assignment ==" b3argsany(0,{x:10},[1,2,3]){|a,b,c|puts "b3argsany called with #{a}, #{b} and #{c}"} puts "" puts "== b3argsany({x:10},{y:20},{z:30},{p:11},{q:22},{r:33}){|a,b,c|...} with multi-assignment ==" b3argsany({x:10},{y:20},{z:30},{p:11},{q:22},{r:33}){|a,b,c|puts "b3argsany called with #{a}, #{b} and #{c}"} puts ""
実行結果
$ ruby proc.rb
== pr.call ==
pr called with
== pr.call(1) ==
pr called with 1
== lm.call(1) ==
lm called with 1
== b2args(1,&pr) ==
Fixnum
#<Proc:0x007fb34511df78@proc.rb:47>
Proc
-- block.call --
pr called with
-- block.call(i) --
pr called with 1
-- yield --
pr called with
-- yield(i) --
pr called with 1
// the end of method
== b2argslm(1,lm) ==
Fixnum
#<Proc:0x007fb34511df50@proc.rb:48 (lambda)>
Proc
-- lambda.call(i) --
lm called with 1
// the end of method
== b1arg(2){|a|...} ==
Fixnum
-- yield --
b1arg called with
-- yield(a) --
b1arg called with 2
== b1arg(0) ==
No block given
== b3argsany([1,2,3]){|a,b,c|...} ==
Array
Array
Hash
-- yield(a,b,c) --
b3argsany called with [1, 2, 3], [] and {}
== b3argsany(*[1,2,3],{x:10}){|a,b,c|...} with multi-assignment ==
Fixnum
Array
Hash
-- yield(a,b,c) --
b3argsany called with 1, [2, 3] and {:x=>10}
== b3argsany(0,{x:10},*[1,2,3]){|a,b,c|...} with multi-assignment ==
Fixnum
Array
Hash
-- yield(a,b,c) --
b3argsany called with 0, [{:x=>10}, [1, 2, 3]] and {}
== b3argsany({x:10},{y:20},{z:30},{p:11},{q:22},{r:33}){|a,b,c|...} with multi-assignment ==
Hash
Array
Hash
-- yield(a,b,c) --
b3argsany called with {:x=>10}, [{:y=>20}, {:z=>30}, {:p=>11}, {:q=>22}] and {:r=>33}
補記:Pythonのyield
プログラムと実行結果
$ cat yield.py def func(n): i=1 while i<=n: yield i i+=1 #raise StopIteration #=> This is not needed. f=func(5) for i in f: print i g=func(5) print g.next() print g.next() print g.next() print g.next() print g.next() try: print g.next() except StopIteration: print "Exception: StopIteration" l=list(func(5)) print l $ python yield.py 1 2 3 4 5 1 2 3 4 5 Exception: StopIteration [1, 2, 3, 4, 5]
補記:JRuby
- JRuby 1.7.21では、キーワード引数に対応していないため、下記のように実行エラーとなった。
$ ~/Downloads/jruby-1.7.21/bin/jruby proc.rb
SyntaxError: proc.rb:38: syntax error, unexpected tPOW
def b3argsany(a,*b,**c)
^
*1:lambdaオブジェクトはブロック内のreturnが呼び出し元のコンテキストでは作用しない、という違いもある→Rubyの ブロック、Proc.new、lambdaの違い - Qiita