http://www.ruby-cn.org/可以參見http://martinfowler.com/bliki/Closures.html
塊(block):只是一段代碼,相當於一個匿名函數;閉包(Closures):一段代碼,能作為參數傳遞給其它方法。
讓我們再回來看看自動點唱機,某些時候,我們需要處理點唱機和使用者的介面:很多按鈕,供使用者選擇歌曲和控制播放,我們需要給這些按鈕指定相應的時間處理代碼,而Ruby中的block就很適合幹這樣的事情。假設點唱機的設計者通過Ruby擴充為我們提供了一個基本的按鈕類。
bStart = Button.new("Start")bPause = Button.new("Pause")# ... |
當使用者按下按鈕之後如何處理呢?Button類提供了一個buttonPressed方法,在按鈕按下時能被回調。所以最簡單的方法建立Button的子類,然後在每個子類中實現buttonPressed 方法。
class StartButton < Button def initialize super("Start") # invoke Button's initialize end def buttonPressed # do start actions... endendbStart = StartButton.new |
這樣做有兩個問題,首先這樣將產生大量子類,如果Button變化了,所有子類都需要維護;第二,按鈕按下之後需要執行的行為不應該在按鈕上表示,這是點唱機的責任。我們可以用塊來消除這些問題
class JukeboxButton < Button def initialize(label, &action) super(label) @action = action end def buttonPressed @action.call(self) endend bStart = JukeboxButton.new("Start") { songList.start } bPause = JukeboxButton.new("Pause") { songList.pause } |
上面代碼的關鍵點在JukeboxButton#initialize的第二個參數,這個參數前面帶有一個&符號,在Ruby中代表這個參數是一個塊,這個塊將會被轉化為一個Proc對象,並關聯道相應變數。在本例中我們把它賦給執行個體變數@action 。當回調方法buttonPressed 執行時,我們用方法Proc#call來調用相應的block。
當建立一個Proc對象之後我們得到的是什麼呢?很有趣,我們得到的不僅是一串代碼。和這個block定義相關聯的上下文環境(範圍內的):self值,定義它的方法,變數,常量等。Ruby中的一個有趣的地方就是即使block定義時候的變數已經不在它的使用範圍內,這個變數還保留著,仍可以使用
我們來看一個例子,這個例子用了方法proc,這個方法把一個block變為了一個Proc對象。
def nTimes(aThing) |
return proc { |n| aThing * n } |
end |
|
p1 = nTimes(23) |
p1.call(3) |
» |
69 |
p1.call(4) |
» |
92 |
p2 = nTimes("Hello ") |
p2.call(3) |
» |
"Hello Hello Hello " |
方法nTimes 返回一個Proc對象,這個對象引用了這個函數的參數:aThing 。即使這個參數在塊被調用時已經不在自己的範圍裡了,這個塊還是可以訪問這個參數。(比如p1.call(4),這個時候,雖然對p1的指派陳述式中的參數23已經超出了範圍範圍,但是它仍然儲存著23×4=92。)