宏与函数

mwa*_*wal 3 lisp macros common-lisp

宏的早期步骤,并寻求澄清。

(defmacro nil! (x)      
  (list 'setf x nil))
Run Code Online (Sandbox Code Playgroud)

保罗·格雷厄姆(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
Run Code Online (Sandbox Code Playgroud)

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

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

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

当然可以

(defun nil!f-a ()
  (setf a nil))
Run Code Online (Sandbox Code Playgroud)

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

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

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

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

我在这里屈服...

Rai*_*wig 6

(defun nil!f (x)
  (setf x nil))
Run Code Online (Sandbox Code Playgroud)

如果这样做,则只能将新的本地变量设置xnil。在普通Common Lisp中,这不可能具有其他效果。

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

(nil! foo)
Run Code Online (Sandbox Code Playgroud)

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

(setf foo nil)
Run Code Online (Sandbox Code Playgroud)

我们可以检查一下:

CL-USER 110 > (macroexpand-1 '(nil! foo))
(SETF FOO NIL)
Run Code Online (Sandbox Code Playgroud)

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

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

对于词汇变量,这是正确的。动态绑定变量可以作为符号传递。

动态绑定:

CL-USER 111 > (flet ((f (sym)
                       (symbol-value sym)))
                (let ((a 10))
                  (declare (special a))
                  (f 'a)))
10
Run Code Online (Sandbox Code Playgroud)

词汇绑定:

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

Error: The variable A is unbound.
Run Code Online (Sandbox Code Playgroud)