我目前正在通过Graham的On Lisp工作,发现这一点很难理解:
装订即可。词汇变量必须直接出现在源代码中。该 例如,
setq
的第一个参数未被评估 在setq
上构建的必须是一个扩展为setq
的宏,而不是一个 调用它的功能。同样适用于像let
这样的运营商 参数将作为lambda表达式中的参数出现 像do这样的宏扩展为let
,等等。任何新的运营商 哪个是改变其参数的词法绑定必须是 写成一个宏。
这来自第8章,其中描述了何时应该而且不应该使用宏来代替函数。
他在这一段中究竟是什么意思?有人会给出一两个具体的例子吗?
非常感谢!
答案 0 :(得分:7)
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
进行评估,但绝不会无条件评估{{1}}或{{1}}。