宏与函数

标签 macros lisp common-lisp

使用宏的早期步骤并寻求说明。

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

Paul Graham 在 ANSI CL 的 p169 上写道

nil!, ntimes and while all have to be written as macros because all have to control the way in which their arguments are evaluated.

我看着 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))

但现在我们失去了将变量传递给这个特定函数的能力,所以它是原始版本的难倒版本。

因此宏允许您在这里做的确切事情是...?

第二个问题,这是真的吗

"In CL you cannot pass a variable into a function"

在这里我屈服......

最佳答案

(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)

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

"In CL you cannot pass a variable into a function"

词法变量也是如此。动态绑定(bind)变量可以作为符号传入。

动态绑定(bind):

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

词法绑定(bind):

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

Error: The variable A is unbound.

关于宏与函数,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/56357179/

相关文章:

macros - 宏扩展到相同的运算符

csv - 将 csv 文件读入列表列表的 Lisp 代码。

c++ - 我可以将此宏更改为内联函数而不影响性能吗?

macros - 如何在模块中定义和使用宏?

lisp - Lisps 何时/为何开始使用分号进行注释?

lisp - 为什么 Setq 工作但不让?

list - LISP:如何从用户那里读取数字并将其存储为列表

lisp - 在 Common Lisp 中转置列表

macros - 如何修复我的第一个 Lisp 宏?

ios - 目标 Interface Builder 的预处理器宏不适用于 OS X?