Rubyのブロック記法は実業務上でもよく使いますが、ともすると「使うだけ」になってしまって、内部でどのようなことが起こっているのか?自分でブロックを渡せるメソッドを設計するにはどうするか?ということを忘れがちになります。
そこで、ブロックに関する仕組みについてまとめてみました。
日常的によく使うブロック
配列の処理などでEnumerable#each でブロック中に処理を書く記法は頻出ですね。
ary = %w(a b c) ary.each do |elem| puts elem end # 1行で記述するなら、{}を使うほうが推奨される ary.each {|elem| puts elem}
ブロックを受け取るメソッドを定義する
ブロックを取り扱うメソッドを定義したい場合は、ブロックを渡したい箇所にyieldを用いることで定義できます。
# ブロックを受け取る関数を定義 def awesome_method puts '----start----' yield puts '----end-----' end # 実際に呼び出す awesome_method do puts 'Block method Called!' end # ----start---- # Block method Called! # ----end-----
ブロックを渡されたかどうかを判定・分岐する
ただ単にyieldを呼び出すと、ブロックを渡されなかった時エラー(LocalJumpError)になります。
ブロックを渡されたかどうかを判定するには、block_given?メソッドを使います。
def awesome_method puts '----start----' if block_given? yield else puts 'no block given.' end puts '----end-----' end awesome_method # ブロックなしでメソッドを呼び出す # ----start---- # no block given. # ----end-----
yieldに引数を渡す
yieldには下記のようにして引数を渡すことが出来ます。
def awesome_method puts '----start----' if block_given? yield Time.now else puts 'no block given.' end puts '----end-----' end awesome_method do |time| puts "now is #{time.to_s}" end # ----start---- # now is 2017-10-08 08:36:53 +0000 # ----end-----
なお、ブロックは呼び出し時に渡された引数の数がブロックの仮引数と異なっていてもエラーになりません。
Procオブジェクトを使ったブロック実行
メソッドの呼び出しで引数の頭に&をつけると、その引数をブロックとして渡すことができます。
def awesome_method(&block) puts '----start----' block.call if block puts '----end-----' end bk = Proc.new do puts 'block given' end awesome_method &bk
Procオブジェクト以外を&で渡した場合は、引数のto_procメソッドを呼んだ結果が実行されます。
class Hoge def initialize(str='hoge') @str=str end def to_proc Proc.new { puts @str.upcase } end end def awesome_method(&block) puts '----start----' block.call if block puts '----end-----' end hoge = Hoge.new awesome_method(&hoge) # => 'HOGE'
よくある記法
下記のコードは、Enumerable#mapを用いて配列の中の要素をすべて大文字に変換しています。
ary = %w(a b c) puts ary.map(&:upcase) #=> A B C
これは、内部的にはSymbol#to_procを呼び出した結果を実行しています。