早期使用宏并进行澄清。
(defmacro nil! (x)
(list 'setf x nil))
Paul Graham写了ANSI CL的p169
nil!
,ntimes
和while
都必须写为宏,因为它们都必须控制对其参数进行求值的方式。
我看着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中,您不能将变量传递给函数”
我在这里屈服...
答案 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.