2016-12-05 502 views
0

defmacro執政宏擴展的順序在http://clhs.lisp.se/Body/m_defmac.htm被證明,但文件是不是完全清楚究竟上,當事情發生。通過實驗CLISP,我發現以下(假設在最高級別定義的所有宏和功能):規則Common Lisp中

  • 直頂級代碼只能調用前面已經定義的宏和功能。

  • 宏或函數內的代碼或由宏生成的代碼可以調用它喜歡的任何函數,包括稍後定義的函數(根據需要支持相互遞歸)。宏內

  • 代碼只能調用比第一宏調用網站先前定義的宏。由宏產生

  • 代碼可以調用後面定義的宏。

Clisp是否僅僅遵循規範,或者在這方面的實現之間是否存在任何差異?

是確切的預期一套規則,及其背後的理由,記錄任何地方?

+1

http://clhs.lisp.se/Body/03_abab.htm –

+2

有關宏的詳細處理,請參閱:http://www.paulgraham.com/onlisp.html 您可以從那裏下載該書。 –

回答

2

你問宏展開 - 但我想澄清的功能是如何優先處理。

注重當調用和定義實際情況。在第二點中,你說功能中的代碼可以調用稍後定義的函數。這不完全正確。

在像C++聲明和定義函數,然後編譯您的應用程序語言。忽略內聯,模板,lambda表達式等神奇功效......,編譯功能時,通過該功能使用的所有其他功能的聲明需要存在 - 和鏈接時,編譯後的定義需要出現 - 所有程序之前開始運行。一旦程序開始運行,所有功能已經完全準備就緒,可以被調用。

現在Lisp中,情況就不同了。現在忽略編譯 - 讓我們來想一下解釋的環境。如果你運行:

;; time 1 
    (defun a() (b)) 
;; time 2 
    (defun b() 123) 
;; time 3 
    (a) 

在時間1你的程序沒有功能。

第一defun然後創建函數(lambda() (b)),並將其與該符號a。此功能包含這個時間點他並沒有叫b一個參考符號b,但a只會調用ba本身被調用。

所以,在時間2你的程序有一個功能,用符號a相關,但尚未執行。

現在第二個defun創建一個函數(lambda() 123),並將它與符號b關聯。

在時間3,您的程序有兩個功能,與符號ab相關聯,但尚未被調用。

現在撥打電話a。在執行過程中,它會查找與符號b,相關的函數,發現此時該函數已存在於此時間點,並調用它。 b執行並返回123.

讓我們添加更多代碼: ;;時間4 defun b()456) ;;時間5 的(a)

時間4之後,一個新的defun創建函數返回456,並將其與該符號相關聯b。這取代了引用b,它保留了返回123的函數,然後垃圾回收(或任何你實現的東西去除垃圾)。

調用a(或者更準確地說,用符號a的功能屬性引用的拉姆達),現在將導致對相反,如果我們最初寫了返回456

調用一個函數:

;; time 1 
    (defun a() (b)) 
;; time 2 
    (a) 
;; time 3 
    (defun b() 123) 

...這將工作過,因爲時間2後,當我們稱之爲a,它無法找到與符號b相關的功能,所以它會失敗。

現在 - compileeval-when,優化等魔法可以做各種從我上述不同的時髦的事情,但要確保你擔心的是更先進的東西之前,首先有這些基本的把握。

  1. 功能僅在該defun被調用的時候創建的。 (解釋器不會「向前看文件」。)
  2. 符號的一個屬性是對函數的引用。 (函數本身實際上並沒有名稱。)
  3. 多個符號可以引用相同的函數。 ((setf (symbol-function 'd) (symbol-function 'b))
  4. 定義函數a調用函數b(通俗的說),只要符號b具有由時間a相關功能被稱爲是OK。 (在defun ning a時不需要)
  5. 符號可以指不同的功能在不同的時間。這會影響「調用」該符號的任何函數。

的宏的規則不同(其擴展是「讀」的時間後靜態),但許多原則保持不變(找他們Lisp的不「的文件中展望」) 。理解Lisp程序比你可能習慣的大多數(較少;-))語言更具動態性和「運行時間」。瞭解什麼發生在執行Lisp程序期間,管理宏擴展的規則將開始有意義。