天天看點

塊和閉包(block and Closures)

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...
  end
end
      
bStart = StartButton.new

這樣做有兩個問題,首先這樣将産生大量子類,如果Button變化了,所有子類都需要維護;第二,按鈕按下之後需要執行的行為不應該在按鈕上表示,這是點唱機的責任。我們可以用塊來消除這些問題

class JukeboxButton < Button
  def initialize(label, &action)
    super(label)
    @action = action
  end
  def buttonPressed
    @action.call(self)
  end
end

 
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。)