我目前正在通过Graham的On Lisp工作,发现这一点很难理解:
捆绑。词汇变量必须直接出现在源代码中。例如,
setq
的第一个参数没有被评估,所以在setq
上构建的任何东西都必须是一个扩展为setq
的宏,而不是一个调用它的函数。同样对于像let
这样的运算符,其参数在lambda表达式中作为参数出现,对于像扩展到let
s的宏等等。任何要更改其参数的词法绑定的新运算符都必须写为宏。
这来自第8章,其中描述了何时应该和不应该使用宏来代替函数。
他在这一段中究竟是什么意思?有人会给出一两个具体的例子吗?
非常感激!
setq
是一种特殊的形式,并没有评估它的第一个论点。因此,如果你想制作一个更新某些东西的宏,你就不能这样做:
(defun update (what with)
(setq what with))
(defparameter *test* 10)
(update *test* 20) ; what does it do?
*test* ; ==> 10
因此,在函数update
setq
内部将变量what
更新为20
,但它是一个局部变量,其值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
参数总是被计算。使用cond
和if
对test
进行评估,并根据then
或else
进行评估,但绝不是无条件评估。