為什麼分兩個步驟:先展開宏,再求值。
因為單單有一個宏定義(這裡稱macro body)是不能在編譯時間求值的,宏的參數還沒呢。宏的參數必須要等到編譯器看到宏調用代碼的時候才能知道。因此將宏參數傳遞給宏定義,然後展開宏再求值,是唯一能行的方法。
很好理解,這裡就像C++的一樣。
反引號(Backquote)`
最簡單的情況下,和‘功能相同,代表不要對一個list求值
`(a list of (+ 2 3) elements) ⇒ (a list of (+ 2 3) elements) '(a list of (+ 2 3) elements) ⇒ (a list of (+ 2 3) elements)
上面兩個運算式是等價的。
下面的宏
(defmacro inc (var) (list 'setq var (list '1+ var)))
用反引號的簡化寫法是:
(defmacro inc2 (var) `(setq ,var (1+ ,var)))
反引號的特殊在於如果在裡面的list中使用,號,會告訴編譯器如何展開該宏:
1.將,後面的求值,
2.用值來替換該處的原運算式
`(a list of ,(+ 2 3) elements) ⇒ (a list of 5 elements)
這裡,(+2 3) 被求值成了5,5就作為一個元素加入到這個list中。
這種用法可以應用在定義宏的時候,請注意差別很大。
(defmacro t-becomes-nil (variable) `(if (eq ,variable t) (setq ,variable nil)))
(macroexpand '(t-becomes-nil foo))
這句宏調用展開後等價於:
(if (eq foo t) (setq foo nil))
如果將宏定義中的,去掉,等價於:
(if (eq varialbe t) (setq variable nil))
此處沒有求值。
`後面的運算式裡面可以用,@符號表示將一個list的值添加進來,比如:
(setq some-list '(2 3)) ⇒ (2 3) `(1 ,@some-list 4 ,@some-list) ⇒ (1 2 3 4 2 3)
可讀性大大加強,另一種可讀性差的寫法:
(cons 1 (append some-list '(4) some-list)) ⇒ (1 2 3 4 2 3)