Common Lisp 宏中的词法绑定

Lexical Bindings in Common Lisp Macros

我目前正在研究 Graham 的 On Lisp,发现这个特别的部分很难理解:

Binding. Lexical variables must appear directly in the source code. The first argument to setq is not evaluated, for example, so anything built on setq must be a macro which expands into a setq, rather than a function which calls it. Likewise for operators like let, whose arguments are to appear as parameters in a lambda expression, for macros like do which expand into lets, and so on. Any new operator which is to alter the lexical bindings of its arguments must be written as a macro.

这来自第 8 章,该章描述了何时应该和不应该使用宏代替函数。

这段话他到底是什么意思?有人可以举一两个具体的例子吗?

非常感谢!

setq 是一种特殊形式,不会计算其第一个参数。因此,如果你想制作一个更新某些东西的宏,你不能这样做:

(defun update (what with)
  (setq what with))

(defparameter *test* 10)
(update *test* 20)        ; what does it do?
*test*                    ; ==> 10

所以在函数内部update setq更新变量what20,但它是一个局部变量,其值为10得到更新,而不是 *test* 本身。为了更新 *test* setq 必须将 *test* 作为第一个参数。宏可以做到这一点:

(defmacro update (what with)
  `(setq ,what ,with))

(update *test* 20)        ; what does it do?
*test*                    ; ==> 20

您可以准确地看到宏展开的结果代码:

(macroexpand-1 '(update *test* 20))
; ==> (setq *test* 20) ; t

类似的例子。您不能使用函数将 if 模拟为 cond

(defun my-if (test then else)
  (cond (test then)
        (t else)))

(defun fib (n)
  (my-if (< 2 n) 
         n
         (+ (fib (- n 1)) (fib (- n 2)))))

(fib 3)

无论你传递什么参数,你都会得到一个无限循环,总是 调用递归案例,因为所有 my-if 参数总是被评估。使用 condiftest 得到评估,并基于 thenelse 被评估,但从来没有无条件地。