Block:
在寫ROR時,大家或多或少會碰到這樣的語法:posts.each do |post| puts post.tiltle end或
posts.each {|post| puts post.tiltle}上面兩段程式碼中用do-end和大括號{}包起來的就是block,但用{}包裹程式碼的方法只適用於程式碼是一行的情況。
學ROR以來有好長一段時間我都不太清楚他的妙用,只覺得這麼普通的東西為什麼要寫的那麼拐彎抹角,但是在看了code school的教學影片後發現他跟javascript的callback機制有異曲同工之妙,這樣的設計讓我們在做一些enumeration操作的時候能將block中的code傳入,之前之所以對這個概念一直搞不清楚就是因為很難具體的想像在each中視怎麼call這段code,現在我們就用這個demo來剖開這個黑盒子:
class Child def initialize @toys=['gun','hammer','puzzle'] end def each_toy for toy in @toys yield toy end end end mark = Child.new mark.each_toy do |toy| puts "I'm playing #{toy}~" end
在這個範例中block像參數一樣被傳入each_toy這個method,並在裡面的loop內被多次呼叫。這樣的設計除了使用起來很靈活、有彈性以外,也能減少重複的程式碼。如果你對javascript比較了解的話可以看這一段程式碼來幫助了解:
function Child() { var toys = ['gun','hammer','puzzle']; this.eachToy = function (cb) { for (var _i in toys) { cb(toys[_i]); } } } mark = new Child(); mark.eachToy( function (toy) { console.log("I'm playing " + toy + "~"); });
但是上面的程式碼中,block看起來一點都不像是物件,也不能被重複使用。如果要像是javascript一樣能將函數像物件一樣被宣告、保存起來,並傳入其他函數作為callback function,我們還少學了一些東西,那就是接下來要介紹的Proc物件。
Proc:
proc物件可以將ruby的程式碼保存起來,在需要的時候再執行他,或當做block傳入其他函數。proc有兩種實作方法:By Proc.new
routine = Proc.new {|variable| Do something... }或
routine = Proc.new do |variable| Do something... end
By lambda
routine = lamda {|variable| Do something... }或
routine = -> {|variable| Do something... }
直接執行
routine.call value
把proc轉成block
要把proc物件轉為block時,我們要在這個物件的前面加上"&",例如:posts.each &routine
在method中判斷是否有block傳入
要知道有沒有傳入block,我們可以使用block_given?來判斷:if block_given? yield else puts "No block past in" end
小結
現在我們就來改寫一下一開始的範例:class Child def initialize @toys=['gun','hammer','puzzle'] end def each_toy if block_given? for toy in @toys yield toy end else puts "No block past in" end end end mark = Child.new routine = lambda { |toy| puts "I'm playing #{toy}~" } mark.each_toy &routine