- Published on
RubyのblockやProcを分かったつもりになっていて見事にハマった
- Authors
- ジャバ・ザ・ハットリ
反省した。Ruby の block や Proc を分かったつもりになっていて、しょうもないところでハマった。自戒を込めてブログに残しておくことにした。
$ ruby -v ruby 2.3.3p222 (2016-11-21 revision 56859) [x86\_64-darwin15]
例1
def method\_1if block\_given? puts 'Yes'yieldelse puts 'No'endendmethod\_1 { puts 'I am a block' }
つまり method_1 に I am a block の出力というブロックを渡して、ブロックが有れば Yes と共にそれを出せと。
method_1 にはブロック引数が無いが、この例のようにそれが問題になることもなく、yield すればちゃんと実行される。
実行結果
$ ruby block\_sample\_1.rb Yes! I am a block
例2
def method\_1if block\_given? puts 'Yes :method\_1'else puts 'No :method\_1'end method\_2enddef method\_2if block\_given? puts 'Yes :method\_2'yieldelse puts 'No :method\_2'endendmethod\_1 { puts 'I am a block' }
block が渡された method_1 から method_2 を呼び出す例。
実行結果
$ ruby block\_sample\_2.rb Yes :method\_1 No :method\_2
渡された block は method_1 まで。method_2 には到達していない。
例3
つまり&引数名にしてブロックを Proc オブジェクト化して渡す必要がある。その対策後のコード例がこれ。
def method\_1 &block if block\_given? puts 'Yes :method\_1'else puts 'No :method\_1'end method\_2 &blockenddef method\_2if block\_given? puts 'Yes :method\_2'yieldelse puts 'No :method\_2'endendmethod\_1 { puts 'I am a block' }
実行結果
これでしっかり block が Proc 化されて method_2 にまで到達していることが分かる。
$ ruby block\_sample\_3.rb Yes :method\_1 Yes :method\_2 I am a block
例4
def method\_1 &block if block\_given? puts 'Yes :method\_1'else puts 'No :method\_1'end method\_2 plus\_one 1, &blockenddef plus\_one number number + 1enddef method\_2 number, &block if block\_given? puts 'Yes :method\_2' puts number yieldelse puts 'No :method\_2'endendmethod\_1 { puts 'I am a block' }
実行結果
$ ruby block\_sample\_4.rb Yes :method\_1 No :method\_2
method_2 にまでブロックが到達していない。これでハマった。とくに例3と変わったことをしているようにも思えない。ただ def plus_one number を加えただけで、そこに block はまったく関係無さそう。なぜ method_2 にまでブロックが到達しないのか、しばらく分からなかった。
見つけた答えがこれ。
例5
def method\_1 &block if block\_given? puts 'Yes :method\_1'else puts 'No :method\_1'end method\_2 plus\_one(1), &blockenddef plus\_one number number + 1enddef method\_2 number, &block if block\_given? puts 'Yes :method\_2' puts number yieldelse puts 'No :method\_2'endendmethod\_1 { puts 'I am a block' }
実行結果
$ ruby block\_sample\_5.rb Yes :method\_1 Yes :method\_2 2 I am a block
例4との違いはここの()だけ。
method\_2 plus\_one(1), &block
つかれた。。。