宏与函数

时间:2019-05-29 09:21:59

标签: macros lisp common-lisp

早期使用宏并进行澄清。

(defmacro nil! (x)      
  (list 'setf x nil))

Paul Graham写了ANSI CL的p169

  

nil!ntimeswhile都必须写为宏,因为它们都必须控制对其参数进行求值的方式。

我看着nil!,想了一下,我想尝试将其作为函数编写。事实证明,他是对的,正如预期的那样,但希望找出原因。如果我愿意

(defun nil!f (x)
  (setf x nil))

(setf a 9)
(nil!f a)
; a is still 9, not nil

强迫自己这样做,我注意到这是对函数的一种奇怪用法,因为通常我不会setf一个参数。如果我要在函数中使用setf,则很有可能是全局变量,而不是参数。

如果我没错,参数x将创建一个新的词法作用域,该作用域将掩盖我在顶层设置的a。意味着我们可以将值传递给函数,但不能传递给变量。

使用宏,我们可以处理变量。

我们当然可以做到

(defun nil!f-a ()
  (setf a nil))

但是现在我们失去了将变量传递给此特定函数的功能,因此它是原始版本的残缺版本。

因此,宏允许您在此处执行的确切操作是...?

第二个问题,这是一个真实的陈述吗

  

“在CL中,您不能将变量传递给函数”

我在这里屈服...

1 个答案:

答案 0 :(得分:6)

(defun nil!f (x)
  (setf x nil))

如果这样做,则只能将新的局部变量x设置为nil。在普通Common Lisp中,这不可能具有其他效果。

(defmacro nil! (x)      
  (list 'setf x nil))

(nil! foo)

上面的表达式将在执行前替换为

(setf foo nil)

我们可以检查:

CL-USER 110 > (macroexpand-1 '(nil! foo))
(SETF FOO NIL)

因此,由于是在执行之前,因此您可以进行各种源(!)操作。

  

“在CL中,您不能将变量传递给函数”

对于词汇变量来说确实如此。动态绑定的变量可以作为符号传递。

动态绑定:

CL-USER 111 > (flet ((f (sym)
                       (symbol-value sym)))
                (let ((a 10))
                  (declare (special a))
                  (f 'a)))
10

词汇绑定:

CL-USER 112 > (flet ((f (sym)
                       (symbol-value sym)))
                (let ((a 10))
                  (f 'a)))

Error: The variable A is unbound.