lisp 閉包

babyyellow發表於2012-03-30
閉包: 閉包是可以包含自由(未繫結到特定物件)變數的程式碼塊;這些變數不是在這個程式碼塊內或者任何全域性上下文中定義的,而是在定義程式碼塊的環境中定義。“閉包” 一詞來源於以下兩者的結合:要執行的程式碼塊(由於自由變數被包含在程式碼塊中,這些自由變數以及它們引用的物件沒有被釋放)和為自由變數提供繫結的計算環境(作用域)。在 Scheme、Common Lisp、Smalltalk、Groovy、JavaScript、Ruby 和 Python 等語言中都能找到對閉包不同程度的支援。


lisp 裡的閉包 並不是刻意造出來的,而是這種語言自帶的。

例如:

CL-USER> (let ((x 0))
       (defun y ()
         (incf x 1)))
 (let ((x 0))
       (defun y ()
         (incf x 1)))
Y
CL-USER> (y)
1
CL-USER> (y)
2
CL-USER> (y)
3
CL-USER>

閉包在這裡是用了全域性名稱空間(global  namespace)來儲存其自由變數的值。

這帶來的一個好處,就可以用閉包來儲存程式碼裡的私有資訊,而不會暴露給其他程式碼

閉包在lisp 裡的可視性是基於語法的(lexical scope)

lexical scope 的好處是這些繫結在編譯後不需要為其實際分配任何記憶體 slot,

 甚至一些顯而易見的邏輯會被直接編譯最佳化成簡單的常量。

閉包在lisp裡可以構造其他oo系統裡的物件或者類的層次,物件變數等等,

在 let over lambda 結構中用 values 返回多個引用了相同 bindings 的 lambdas,可以實現 class 和 object.

下面的是一段抄來的程式碼:
? (let ((password nil)
(secret nil))
(defun set-password (new-passwd)
(if password
'|Can't - already set|
(setq password new-passwd)))
(defun change-password (old-passwd new-passwd)
(if (eq old-passwd password)
(setq password new-passwd)
'|Not changed|))
(defun set-secret (passwd new-secret)
(if (eq passwd password)
(setq secret new-secret)
'|Wrong password|))
(defun get-secret (passwd)
(if (eq passwd password)
secret
'|Sorry|)))
GET-SECRET
? (get-secret 'sesame)
|Sorry|
? (set-password 'valentine)
SECRET
? (set-secret 'sesame 'my-secret)
|Wrong password|
? (set-secret 'valentine 'my-secret)
MY-SECRET
? (get-secret 'fubar)
|Sorry|
? (get-secret 'valentine)
MY-SECRET
? (change-password 'fubar 'new-password)
|Not changed|
? (change-password 'valentine 'new-password)
NEW-PASSWORD
? (get-secret 'valentine)
|Sorry|
; The closed-over lexical variables aren't in the global environment
? password
Error: unbound variable
? secret
Error: unbound variable
; The global environment doesn't affect the closed-over variables
? (setq password 'cheat)
CHEAT
? (get-secret 'cheat)
|Sorry|

來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/133735/viewspace-719927/,如需轉載,請註明出處,否則將追究法律責任。