【Ruby】blockとprocとlambda 入門
Ruby の block と proc と lambda の違いをまとめました.
block とは
do ~ end または {} に囲われた部分.
method に処理を渡すために使われる.
# do-end block 10.times do |i| p i end # {} block [:sun, :mon, :tue].map{ |w| p w.to_s.upcase } # call block def hoge if block_given? yield else p 'No block' end end hoge { p 'hogeeee!' } #=> "hogeeee!" hoge #=> "No block"
method内でyieldを使うと,
渡されたblock(処理)を実行できる.
methodにblockが渡されたかどうかは,
block_given?で boolean が返ってくる.
Proc とは
Procは処理をinstance化して,
objectとして扱うためのもの.
Proc は Ruby の class クラスの object.
なので new method で instance化できる.
その際に処理をblockで渡す.
そのため proc のobjectをmethodに渡す事で,
blockと同様 method に処理を渡すことも出来る.
proc objectに対して call methodを呼ぶと,
proc objectの処理をよびだせる.
# instantiate Proc # pass block to Proc#new a = Proc.new { p 'hogeee!' } b = Proc.new do |txt| p txt end # call proc a.call #=> "hogeee!" # call proc with args b.call(:tarooo) #=> "tarooo"
method内でblock を procとして受け取る
method内でyield を使うと,
渡されたblockの処理を実行できました.
渡されたblockをmethod内でproc objectとして,
受け取る方法が存在します.
methodの引数定義の際に, 引数の名前の前に&をつけます.
&をつける引数は必ず, 「引数定義の最後の引数」です.
def hoge(&proc) p proc.class p proc.call 'method end' end hoge { 'string in block' } # Proc (p proc.class) # "string in block" (p proc.call) #=> "method end" (return value)
method内でproc を blockとして受け取る
さっきの逆で引数として渡したproc objectを,
blockとして受け取る.
procを引数に渡す際に &をつける事で可能.
def hoge yield # block の実行 proc objectの実行(= call methodの呼び出し)ではない end p = Proc.new { p 'string in proc' } hoge(&p) # &をつける. # hoge methodは引数を定義してないのに # proc objectを引数 **みたい** に(blockとして)渡せる. #=> "string in proc"
様々なproc objectの生成方法
様々な方法でProc クラスのobjectを生成できる.
# symbolに対して to_proc method p1 = :hoge.to_proc p.class #=> Proc # [method] method # 引数にsymbolを渡すと, # receiverのobject(省略時はself)に対して # 引数のsymbol名のmethodをproc化して返す. def hoge p 'hogeee!!' end p2 = method(:hoge) p2.class #=> Proc p2.call #=> "hogeee!!"
lambdaとは
lamndaはKernel moduleの **method** である.
p Kernel.methods.select { |m| m.to_s.include?('lambda') } #=> [:lambda]
そしてlambda methodの返り値は Proc クラスの インスタンス!!
Proc new された proc object と
lambdaで作られた proc objectには
引数, returnの扱いで差がある.
その違いについては今回は割愛.
s = lambda { p 'hogeee!' } s.class #=> Proc s.call #=> "hogeee!" # alias of lambda s = -> { p :hoge } s.class #=> Proc s.call #=> "hoge"
おまけ
実はここまでの記事をちゃんと読み内容を理解していても
rails 開発で頻繁に出てくる次のコードを理解できない.
「method内でproc を blockとして受け取る」で紹介した,
procをmethodに引数みたいに渡す時に付けた& .
紹介した通りこいつはprocの頭につけるものだが,
symbolの頭につけるとそのsymbolをprocとして扱ってくれる.
(symbolにto_proc methodを呼び出したかのように)
['sun', 'mon', 'tue'].map(&:upcase) #=> ["SUN", "MON", "TUE"] # ['sun', 'mon', 'tue'].map(&(:upcase.to_proc)) # と同じ意味