Common Lisp宏中的词法绑定

时间:2018-03-12 21:53:41

标签: macros lisp common-lisp lexical-scope

我目前正在通过Graham的On Lisp工作,发现这一点很难理解:

  

装订即可。词汇变量必须直接出现在源代码中。该   例如,setq的第一个参数未被评估   在setq上构建的必须是一个扩展为setq的宏,而不是一个   调用它的功能。同样适用于像let这样的运营商   参数将作为lambda表达式中的参数出现   像do这样的宏扩展为let,等等。任何新的运营商   哪个是改变其参数的词法绑定必须是   写成一个宏。

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

他在这一段中究竟是什么意思?有人会给出一两个具体的例子吗?

非常感谢!

1 个答案:

答案 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个参数。使用condif test进行评估,并根据thenelse进行评估,但绝不会无条件评估{{1}}或{{1}}。