ジャバ・ザ・ハットリ

ベルリンのスタートアップで働くソフトウェアエンジニア

RubyのblockやProcを分かったつもりになっていて見事にハマった

2017-04-23プログラミング

Loading...

反省した。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

つかれた。。。