2012-12-25 74 views
18

問題不在使用關鍵字,但實際上關於關鍵字執行。例如,當我創建關鍵字參數的一些功能,並撥打電話:關鍵字如何在Common Lisp中工作?

(defun fun (&key key-param) (print key-param)) => FUN 
(find-symbol "KEY-PARAM" 'keyword) => NIL, NIL ;;keyword is not still registered 
(fun :key-param 1) => 1 
(find-symbol "KEY-PARAM" 'keyword) => :KEY-PARAM, :EXTERNAL 

如何關鍵字來傳遞參數?關鍵字是值本身的符號,所以如何使用關鍵字來綁定相應的參數?

有關關鍵字的另一個問題 - 關鍵字用於定義包。我們可以定義一個與已經存在的關鍵字命名的包:

(defpackage :KEY-PARAM) => #<The KEY-PARAMETER package, 0/16 ... 
(in-package :KEY-PARAM) => #<The KEY-PARAMETER package, 0/16 ... 
(defun fun (&key key-param) (print key-param)) => FUN 
(fun :KEY-PARAM 1) => 1 

如何區分系統的軟件包名稱和功能參數名之間:KEY-PARAM的使用情況如何? 此外,我們可以做出更復雜的東西,如果我們定義功能KEY-PARAM並將其導出(實際上不起作用,但名稱):

(in-package :KEY-PARAM) 
(defun KEY-PARAM (&key KEY-PARAM) KEY-PARAM) => KEY-PARAM 
(defpackage :KEY-PARAM (:export :KEY-PARAM)) 
    ;;exporting function KEY-PARAM, :KEY-PARAM keyword is used for it 
(in-package :CL-USER) => #<The COMMON-LISP-USER package, ... 
(KEY-PARAM:KEY-PARAM :KEY-PARAM 1) => 1 
    ;;calling a function KEY-PARAM from :KEY-PARAM package with :KEY-PARAM parameter... 

的問題是一樣的,Common Lisp中如何做區分關鍵字:KEY-PARAM的使用在這裏?

如果在Common Lisp中有關於關鍵字的一些手冊以及它們的機制的解釋,如果您在此發佈鏈接,我將不勝感激,因爲我只能找到一些關於關鍵字使用的簡短文章。

回答

10

有關關鍵字參數的詳細信息,請參閱Common Lisp Hyperspec。請注意,

​​

實際上是短期的:

(defun fun (&key ((:key-param key-param))) ...) 

關鍵字參數的完整語法是:

((keyword-name var) default-value supplied-p-var) 

default-valuesupplied-p-var是可選的。雖然傳統上使用關鍵字符號作爲keyword-name,但這不是必需的;如果您只指定var而不是(keyword-name var),則它將默認keyword-name作爲關鍵字包中的一個符號,其名稱與var的名稱相同。

因此,舉例來說,你可以這樣做:

(defun fun2 (&key ((myoption var))) (print var)) 

,然後將其稱爲:

(fun 'myoption 3) 

它的內部工作原理是被調用函數時的方式,通過步驟參數列表,收集參數對<label, value>。對於每一個label,它看起來在與keyword-name參數的參數列表,並結合相應的varvalue

我們通常使用關鍵字的原因是因爲:前綴脫穎而出。這些變量已經進行了自我評估,因此我們不必引用它們,即你可以寫:key-param而不是':key-param(FYI,後者符號是在早期的Lisp系統必要的,但CL設計師決定是醜陋和冗餘)。而且我們通常不會使用該變量來指定與變量名稱不同的關鍵字,因爲這會造成混淆。這是完全通用的。此外,允許使用常規符號代替關鍵字對於像CLOS這樣的設施非常有用,在這些設施中參數列表會被合併,並且您希望避免衝突 - 如果您要擴展通用函數,則可以添加其關鍵字在您自己的包中並且在那裏的參數不會碰撞。

定義包和導出變量時使用的關鍵字參數又僅僅是一個慣例。 DEFPACKAGEIN-PACKAGEEXPORT等只關心他們給出的名稱,而不是它在什麼包。你可以寫

(defpackage key-param) 

,它通常會很好的工作。許多程序員不這樣做的原因是因爲它在自己的包中實現了一個符號,並且這有時會導致包衝突,如果這與名稱試圖從其他包導入的符號相同。使用關鍵字將這些參數從應用程序包中分離出來,從而避免這樣的潛在問題。

底線是:當你使用一個符號,你只關心它的名字,而不是它的身份,這是最安全的經常使用的關鍵字。

最後,關於區別的關鍵字時,他們以不同的方式使用。關鍵字只是一個符號。如果在函數或宏只需要一個普通參數的地方使用它,則該參數的值將是符號。如果您正在調用具有參數&key的函數,那麼這是他們唯一用作標籤來將參數與參數關聯的時間。

+0

謝謝!現在一切都很清楚,這真的很有幫助! – TheEnt

+1

@Barmar *「**(defun fun(&key key-param)...)**實際上是:**(defun fun(&key((:key-param key-param)))...) **「*,除了第一個之後,一個名爲」KEY-PARAM「的符號尚未在關鍵字包中實現(至少根據OP的抄本)。如果它是擴展arglist的簡寫(也許一個實現可以這樣做;我不確定它是否被允許),那麼關鍵字將在宏擴展時被實施。 (這不是特別重要,但它確實指出了「泄漏」(或不)defun的一些實現。) –

1

沒有必要爲「系統」來區分不同的關鍵字使用。它們只是用作名稱。與language-score使用時

(defun language-score (language &optional (language-scores *language-scores*)) 
    (getf language-scores language)) 

關鍵字,指定不同的編程語言::例如,假定有兩個的Plist:

(defparameter *language-scores* '(:basic 0 :common-lisp 5 :python 3)) 
(defparameter *price* '(:basic 100 :fancy 500)) 

一個函數產生一個語言得分

CL-USER> (language-score :common-lisp) 
5 

現在,系統會如何區分*language-scores*中的關鍵字與*price*中的關鍵字?絕對沒有。關鍵字只是名稱,在不同的數據結構中指定不同的東西。它們不像自然語言中的同音詞那樣出衆 - 它們的使用決定了它們在特定環境中的含義。

在上面的例子中,沒有什麼能阻止我們使用功能與錯誤的上下文:

(language-score :basic *prices*) 
100 

語言沒有什麼可以阻止我們這樣做,而關鍵字的不那麼花哨的編程語言和不那麼奇特的產品都是一樣的。

有很多可能性,以防止這種情況:沒有允許在首位language-score可選參數,把*prices*另一個包沒有外擴它收盤,詞彙的結合,而不是使用全局特殊*language-scores*而只露出手段添加和檢索條目。也許只是我們對代碼庫或約定的理解足以阻止我們這樣做。重點是:系統區分關鍵字本身並不是實現我們想要實現的目標所必需的。

你問的具體關鍵字並沒有什麼不同:實現可能會將關鍵字參數的綁定存儲在alist,plist,hash-table或其他任何內容中。對於軟件包名稱,關鍵字只用作軟件包標識符,而軟件包名稱可以用字符串(大寫)代替。實現是否將字符串轉換爲關鍵字,將關鍵字轉換爲字符串,還是內部完全不同的東西並不重要。重要的僅僅是名稱,以及它在哪個上下文中使用。

+0

謝謝! 還有一個問題,是否可以檢查實現是否將關鍵字轉換爲字符串?是可以減少關鍵字字符串?我的意思是當Lisp閱讀器發現它解析它的關鍵字時,它只是一種特殊的字符串。 – TheEnt

+0

爲了檢查實現如何在內部工作,您必須查看源代碼(或詳細的實現特定文檔)。在包名的情況下,我會假設實現來比較字符串,因爲否則(使用默認閱讀器設置)情況信息將會丟失。雖然兩個包「foo」和「Foo」不同,但兩個關鍵字':foo'和':Foo'不是。他們都是用相同的「符號名」「FOO」進行實習。 (但是,也有':| Foo |'來防止這種情況。) – danlei

4

好手冊是Chapter 21 of PCL

回答您的問題簡要:

  • 關鍵字遠銷符號keyword包,讓您可以參考他們不僅爲:a,也可作爲keyword:a

  • 在功能參數關鍵字列表(稱爲lambda-list s)可能以下列方式實施。在&key改性劑存在的lambda形式擴展到與此類似:

    (let ((key-param (getf args :key-param))) 
        body) 
    
  • 當您使用關鍵字來命名包它實際上是作爲一個string-designator。這是一個Lisp概念,允許傳遞給某個處理字符串的函數,以後將用作符號(對於包,類,函數等的不同名稱)不僅包括字符串,還包括關鍵字和符號。因此,定義/使用的軟件包的基本途徑實際上是這樣的:

    (defpackage "KEY-PARAM" ...) 
    

    但是你可以和使用:

    (defpackage :key-param ...) 
    

    (defpackage #:key-param ...) 
    

    (這裏#:被讀者宏創建無間斷的符號;這種方式是首選的,因爲您不會在流程中創建不需要的關鍵字)。

    後兩種形式將轉換爲大寫字符串。因此,一個關鍵字保留一個關鍵字,並且一個包將其命名爲字符串,並從該關鍵字轉換而來。

總結起來,關鍵字有自己的價值,以及任何其他符號。區別在於關鍵字不需要使用keyword軟件包或其明確用法的明確資格。作爲其他符號,它們可以作爲對象的名稱。例如,您可以使用關鍵字命名一個函數,並且在每個包中都可以「神奇」地訪問:)有關詳細信息,請參閱@ Xach的blogpost

+0

謝謝! '(getf:key-param args)' - 非常好的解釋。另外,我嘗試了關鍵字命名的函數,在Lispworks中,類似於'(defun:test()「hi」)''會導致錯誤'定義函數:TEST可以從KEYWORD包中看到。「(但有一個調試器選項」Define it anyway 「),而在SBCL中一切正常。 – TheEnt

+0

歡迎光臨!我也必須說,我不小心寫錯了順序 - 正確的方法是'(getf args:key-param)':) –