2012-11-15 59 views
2

我對一個宏和一個函數之間的區別感到困惑;具體來說爲什麼第二部分在第一部分成功之後失敗。如何將宏存儲在Common Lisp [sbcl]中的變量中?

(defun foo() "foo") 
(setf a 3) ;; sets the symbol value cell to 3 
(setf a #'foo) ;; PART ONE 
(funcall a) ;; returns foo 

(defmacro bar() "bar") 
(setf b #'bar) ;; Error the macro name bar was found as an argument to function PART TWO 

回答

2

# (sharpsign)是一個標準的宏字符,它是一個調度宏字符。它應該與另一個角色組成。 #' (sharpsign single-quote)組合在它後面需要一個函數名稱或lambda表達式,並且它將展開爲(function expression)

因此,#'foo在讀取時被擴展爲(function foo)。如果foo是一個函數,function將評估它。在一個詞彙範圍內,它可能是一個foo,由flet or labels連接。如果沒有這樣的詞彙定義,它會嘗試從符號的函數中獲取全局函數定義。

現在,(function bar)表示bar代表一個宏,可能是詞彙macrolet或全局defmacro,表示錯誤。但是,您可以使用(macro-function 'bar)來獲取全局bar宏的宏功能。如果存在,它是兩個參數的函數:表單和環境。

除非你打算將bar的宏函數應用到窗體上,否則它可能不是你想要的。讓我們考慮應用and的宏函數:它不會執行邏輯布爾操作,它可能會將給定表單擴展爲if

但是,如果這是您想要的,請記住macro-function具有第二個可選參數環境。您可能在defmacrodefine-setf-expander中獲得環境參數。在後者中,通常需要get-setf-expansion考慮擴展子表單時的詞彙環境。

嘗試了這一點:

(funcall (macro-function 'and) '(and form1 form2 form3) nil) 

練習:實現自己的macroexpand-1 and macroexpand

練習:實現一個macroexpand-all,一個macroexpand,它遞歸到子表單中,識別Common Lisp的特殊運算符。

注意:不要太過於使用macroexpand-all,它需要代碼執行程序,它是特定於實現的代碼。

+0

看來我的問題的原意似乎可以通過以下方法完成:(setf(宏函數'sym1)(宏函數'bar)) – tjb

1

這樣做的原因是,宏都沒有的功能,所以你不能把它們稱爲給函數(#'foo(function foo)的簡寫,它得到了相應的功能對象符號foo),和你他們不能funcall

部分解決方案是將宏包裝在一個函數中,(setf b (lambda() (bar)))將起作用,同樣funcall。但如果您期望宏中的參數&rest/&body,它將不起作用。在這種情況下,沒有通用的方法來實現,你想要什麼。所以你需要重新考慮你的方法。現在的問題是:你爲什麼試圖對宏進行funcall?也許,一個普通的函數會適合你的情況?

7

宏不是功能。因此你不能從宏名稱中獲取函數對象。你可以應用函數,但不能使用宏。

宏需要源代碼並生成新的源代碼。

Common Lisp的定義是這樣的,它可以在運行前在編譯時完成。在一般情況下,Common Lisp不支持運行時宏擴展。 Common Lisp這樣做,以便代碼在運行之前可以完全編譯。定義Common Lisp的目標之一是將其定義爲一種允許高效執行大型Lisp程序的語言。運行時代碼生成僅在受控方式下有用 - 否則在運行時可能會發生全新的執行錯誤。不允許使用允許通用運行時代碼操作的宏機制。

在較老的Lisp方言中,有一種叫做FEXPRs的想法,它類似於可以在運行時調用的宏,並且可以將源操作爲運行時。 Common Lisp已刪除此功能。關於此的背景,請參閱Kent Pitman的Special Forms in Lisp

0

它可能會幫助宏的東西作爲'編譯器插件'。現在,將編譯器的一部分存儲在變量中是否有意義?沒有(甚至在Lisp中也沒有)。