在大多數情況下最好避免#因爲它「大部分」是不必要的,並且使得代碼更加冗長。有些情況下需要引用某種形式的引用(請參見下面的示例4)。
注:本文中的所有示例都已在Emacs Lisp(GNU Emacs 25.2.1)中進行了測試,但它們應該在任何ANSI通用lisp中以相同的方式工作。兩種方言的基本概念是相同的。
簡單的解釋
首先,讓我們研究的情況下的時候,最好避免引用。函數是第一類對象(例如像任何其他對象一樣對待,包括將它們傳遞給函數並將它們分配給變量的能力),這些對象會評估自己。匿名函數(例如lambda表單)就是這樣一個例子。在Emacs Lisp(M-x ielm RET)或任何ANSI通用lisp上嘗試以下內容。
((lambda (x) (+ x 10)) 20) -> 30
現在,嘗試引用的版本
(#'(lambda (x) (+ x 10)) 20) -> "function error" or "invalid function..."
如果您使用堅持使用#」,你必須寫
(funcall #'(lambda (x) (+ x 10)) 20) -> 30
詳細解釋
要真正當理解引用是必需的,必須知道Lisp如何評估表達式。請繼續閱讀。我保證做這個簡潔。
您需要了解Lisp的幾個基本事實:
- Lisp的 「永遠」 計算每一個表情。那麼,除非表達是引用的,在這種情況下,它將返回未評估。
- 原子評價自己。原子表達式不是列表。例子包括數字,字符串,哈希表和向量。
- 符號(變量名稱)存儲兩種類型的值。他們可以保持常規值和功能定義。因此,Lisp符號有兩個稱爲單元格的槽來存儲這兩種類型。非功能性內容通常保存在符號的值單元格中,並在函數單元格中運行。同時持有非功能性和功能性定義的能力將Emacs Lisp和Common Lisp放在2-Lisp類別中。在表達式中使用兩個單元格中的哪一個取決於如何使用該符號 - 更具體地說是它在列表中的位置。相比之下,Lisp的一些方言中的符號,Scheme是最有名的,它只能保存一個值。 Scheme沒有價值和功能細胞的概念。這樣的Lisp統稱爲1-Lisp。
現在,您需要大致瞭解Lisp如何評估S表達式(括號表達式)。每個S-表達大致如下評價:
- 如果被引用,返回它不計算
如果不帶引號的,得到它的CAR(例如,第一個元素),並使用以下規則評價它:
一。如果原子只是返回它的值(例如3→3,「pablo」 - >「pablo」)
b。如果是S表達式,則使用相同的整體程序對其進行評估。如果符號返回其功能單元的內容,則返回S表達式的CDR中的每個元素(例如,除列表的第一個元素外的所有元素)。
- 將從CAR獲得的函數應用於從CDR中每個元素獲得的值。
上面的過程意味着,在一個未加引號 S-表達CAR任何符號必須在其功能細胞有效的功能性定義。
現在,讓我們回到帖子開頭的例子。爲什麼
(#'(lambda (x) (+ x 10)) 20)
產生錯誤?它會這樣做,因爲#'(lambda(x)(+ x 10))是S表達式的CAR,由於功能引用#',Lisp解釋器不對其進行評估。
#'(lambda (x) (+ x 10))
不是一個函數,而是
(lambda (x) (+ x 10))
是。請記住,引用的目的是爲了防止評估。另一方面,拉姆達形式評估自己,一個功能形式,它作爲一個有效的列表的CAR。當Lisp的評估的
((lambda (x) (+ x 10)) 20)
的CAR它得到(拉姆達(X)(+×20)),其是可以在一個列表被應用到的參數的其餘部分的功能(提供的長度CDR等於lambda表達式允許的參數數量)。因此,
((lambda (x) (+ x 10)) 20) -> 30
問題是什麼時候引用函數或符號來保存功能定義。除非你「不正確地」做事,否則幾乎沒有答案。通過「不正確」,我的意思是說,當你應該做相反的事時,你將一個功能定義放置在符號的值單元格或功能單元格中。請參見下面的示例來更好地理解:
例1 - 中值單元存儲功能
假設你需要使用「應用」與預計可變數量的參數的函數。一個這樣的例子是符號+。 Lisp將+視爲常規符號。功能定義存儲在+的功能單元中。你可以,如果你喜歡使用
(setq + "I am the plus function").
如果評估
+ -> "I am the plus function"
但是值分配給它的值單元格,(1 + 2)仍正常工作。
(+ 1 2) -> 3
函數apply在遞歸中非常有用。假設你想對列表中的所有元素求和。你不能寫
(+ '(1 2 3)) -> Wrong type...
原因是+期望它的參數是函數。申請解決此問題
(apply #'+ '(1 2 3)) -> (+ 1 2 3) -> 6
爲什麼我報價+上面?記住我上面概述的評估規則。 Lisp通過檢索存儲在其函數單元中的值來評估符號apply。它會得到一個可以應用於參數列表的函數過程。但是,如果我不引用+,Lisp將檢索存儲在其值單元中的值,因爲它不是S表達式中的第一個元素。因爲我們將+的值單元設置爲「我是加函數」,所以Lisp沒有得到+函數單元中保存的函數定義。實際上,如果我們沒有將它的值單元設置爲「我是加函數」,那麼Lisp將根據apply的要求檢索nil,這不是函數。
有沒有辦法使用+不帶引號的應用。就在這裏。你可以只評估了下面的代碼:
(setq + (symbol-function '+))
(apply + '(1 2 3))
這將計算爲6,預期因爲Lisp的評估板(應用+'(1 2 3)),現在發現+的存儲+功能定義的價值細胞。
例2 - 在值單元格中存儲功能定義
假設你存儲在符號的價值細胞的功能定義。這是實現如下:
(setq AFunc (lambda (x) (* 10 x)))
評估
(AFunc 2)
因爲Lisp語言不能找到AFunc的功能細胞的功能產生錯誤。通過使用funcall來解決這個問題,它告訴Lisp使用符號的值單元格中的值作爲功能定義。你用「funcall」來做到這一點。
(funcall AFunc 2)
假設存儲在符號的值單元中的功能性定義是有效的,
(funcall AFunc 2) -> 20
你可以通過使用FSET放置拉姆達形式在符號的功能細胞避免使用funcall具有:
(setf AFunc (lambda (x) (* 10 x)))
(AFunc 2)
此代碼塊將返回20,因爲lisp在AFunc的函數單元中找到了一個功能性定義。
例3 - 局部功能
假設你正在編寫一個函數,需要將無法在其他地方使用的功能。典型的解決方案是定義一個僅在主要範圍內有效的函數。嘗試:
(defun SquareNumberList (AListOfIntegers)
"A silly function with an uncessary
local function."
(let ((Square (lambda (ANumber) (* ANumber ANumber))))
(mapcar Square AListOfIntegers)
)
)
(SquareNumberList '(1 2 3))
此代碼塊將返回
(1 4 9)
原因廣場未在上面的例子中引述的是,S形表達式根據我上面概述的規則評估。首先,Lisp提出了mapcar的功能定義。接下來,Lisp抽取第二個參數的內容(例如'Square)值單元格。最後,它返回'(1 2 3)未評估的第三個參數。
例4 - 一個符號價值的內容和功能的細胞
這裏是需要引號時,一個情況。
(setq ASymbol "Symbol's Value")
(fset 'ASymbol (lambda() "Symbol's Function"))
(progn
(print (format "Symbol's value -> %s" (symbol-value 'ASymbol)))
(print (format "Symbol's function -> %s" (symbol-function 'ASymbol)))
)
上面的代碼將評估爲
"Symbol's value -> Symbol's Value"
"Symbol's function -> (lambda nil Symbol's Function)"
nil
報價需要在兩個
(fset 'ASymbol (lambda() "Symbol's Function"))
和
(symbol-value 'ASymbol)
和
(symbol-function 'ASymbol)
因爲否則Lisp在每種情況下都會得到ASymbol的值,從而防止fset,符號值和符號函數正常工作。
我希望這篇冗長的文章對某人有用。
我知道命名空間的區別。但是我期望,因爲lambda直接返回一個函數對象(或者是它),所以不需要調用'function'或#'。爲什麼這樣? – 2010-07-24 15:16:25
更新了答案。 – danlei 2010-07-24 15:53:01
嗯,所以不,純lambda不返回一個函數對象...謝謝你的解釋。 – 2010-07-24 16:35:55