
Lisp-1 vs Lisp-2[1]
Scheme的求值模型非常簡單:只是用一個名字空間,所有表達式中相應位置的值應該是明確的。
Common Lisp和Scheme最大的不同是,函數名字空間和數據的名字空間是分離的。操作函數名字空間的語句包括,defun,flet,labels,defmethod和defgeneric等方法。要用函數名作為另一個函數的參數來傳遞函數時,需要使用function特殊操作符或者使用#' 。
我們稱Scheme的變量模型為Lisp-1,Common Lisp的變量模型是Lisp-2.
綁定
在編程語言中綁定是變量名(變量標識符)和對象(保存于內存中的存儲單元,數據或代碼)的映射關系。在這個綁定過程中是作用域有密不可分的關系,作用域決定了哪個變量綁定了哪個存儲單元。
為變量建立綁定之后,就可以通過變量名來引用其所綁定的值。綁定的具體含義,可以參考下圖

詞法作用域(lexical scoping)
詞法作用域又叫靜態作用域(static scope)。顧名思義,詞法變量即是使用詞法作用域的變量。在詞法作用域里,一個變量的變量名只能在一個函數或一段代碼區域( block )內存在,此時變量名才會綁定到變量的值。
詞法變量擁有不確定的生存期,即從時間上來講,一個詞法變量可以在任意的時間里持續存在,取決于該變量需要被使用(reference)多久。 詞法作用域里,對于函數體中的一個符號,不會逐層檢查函數的調用鏈,而是檢查函數定義時的外部環境,即捕捉的是函數定義時該符號的綁定。
動態作用域(dynamic scoping)
使用動態作用域的變量叫做動態(dynamic)變量,有時也叫做特殊(special)變量。動態作用域里,每個變量名(變量標識符)都擁有一個全局的綁定棧。引入一個與動態變量同名的局部變量會為此變量名創建一個新的變量綁定并將其壓入此變量名的全局綁定棧中,一個全局的變量名(變量標識符)總是引用當前其棧頂的綁定,當使用該變量綁定的代碼執行完畢(即程序控制流離開了此變量的作用域),該變量綁定就會從此變量名的全局綁定棧中被彈出,該變量綁定就失效。
動態作用域表示的范圍是不確定的,可從任何位置訪問一個動態變量,取決于它們在什么地方被綁定。動態變量擁有動態的生存期。因容易引起誤會而需要注意的是,不確定的作用域和動態生存期的組合經常被錯誤地稱為動態作用域(dynamic scope)。
動態作用域里,函數執行遇到一個符號,會由內向外逐層檢查函數的調用鏈,并打印第一次遇到的那個綁定的值。最外層的綁定即是全局狀態下的那個值。
Common Lisp的例子
請看下面的代碼
(let ((y 7))(defun foo (x)(print x)(print y)))(let ((y 5))(foo 1))
我們通過SLIM執行后,可以得到輸出1和7,這說明Common Lisp使用的是詞法作用域。在foo中尋找y的綁定時,它檢查函數foo的詞法上下文。
再請看下面代碼
(let ((y 7))(defun foo (x)(print x)(print y)(setq y (+ y 2))))(let ((y 5))(foo 1)(foo 1))(let ((y 5))(foo 2))
我們通過SLIM執行后,我們會看到,1,7,1,9,2和11。在例子中的第一個let表里,定義了一個變量,符號名為y并綁定了值7,那么這個y的作用域就是這個let表區域。 foo函數定義在這個區域內,其內部會使用到一個符號名為y的變量。 那么在詞法作用域的情況下,當foo被調用時,其會查找其定義的環境有沒有符號名y的變量可以綁定,如果有則把foo中符號y的值綁定,在這里就是7。 并且這里foo中的y和外部let中的y共享一個值,都是對這個值的引用,并不是拷貝了一個新值。
參考
- ^https://en.wikipedia.org/wiki/Common_Lisp#The_function_namespace