Common Lisp宏中的词法绑定

问题描述 投票:3回答:1

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

捆绑。词汇变量必须直接出现在源代码中。例如,setq的第一个参数没有被评估,所以在setq上构建的任何东西都必须是一个扩展为setq的宏,而不是一个调用它的函数。同样对于像let这样的运算符,其参数在lambda表达式中作为参数出现,对于像扩展到lets的宏等等。任何要更改其参数的词法绑定的新运算符都必须写为宏。

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

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

非常感激!

macros lisp common-lisp lexical-scope
1个回答
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

一个类似的例子。你不能使用函数模拟ifcond

(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进行评估,但绝不是无条件评估。

© www.soinside.com 2019 - 2024. All rights reserved.