macros - 为什么这个 Lisp 宏作为一个整体可以工作,即使每个部分都不起作用?

标签 macros lisp common-lisp gensym

我正在阅读/学习 Practical Common Lisp。我正在阅读有关在 Lisp 中构建测试框架的章节。

我按如下方式实现了函数“test-+”,并且它有效:

(defun test-+ ()
  (check
    (= (+ 1 2) 3)
    (= (+ 5 6) 11)
    (= (+ -1 -6) -7)))

记住,我说过,它有效,这就是为什么接下来的内容如此令人困惑....

这里是“test-+”所指的一些代码:

(defmacro check (&body forms)
  `(combine-results
    ,@(loop for f in forms collect `(report-result ,f ',f))))

(defmacro combine-results (&body forms)
  (with-gensyms (result)
    `(let ((,result t))
       ,@(loop for f in forms collect `(unless ,f (setf ,result nil)))
       ,result)))

(defmacro with-gensyms ((&rest names) &body body)
  `(let ,(loop for n in names collect `(,n (gensym)))
     ,@body))

(defun report-result (value form)
  (format t "~:[FAIL~;pass~] ... ~a~%" value form)
  value)

现在,我一直在做的是使用 Slime 逐步宏扩展这些(使用映射到 macroexpand-1 的 ctrl-c RET)。

因此,“test-+”的“check”调用扩展为:

(COMBINE-RESULTS
  (REPORT-RESULT (= (+ 1 2) 3) '(= (+ 1 2) 3))
  (REPORT-RESULT (= (+ 5 6) 11) '(= (+ 5 6) 11))
  (REPORT-RESULT (= (+ -1 -6) -7) '(= (+ -1 -6) -7)))

然后 that 宏扩展为:

(LET ((#:G2867 T))
  (UNLESS (REPORT-RESULT (= (+ 1 2) 3) '(= (+ 1 2) 3)) (SETF #:G2867 NIL))
  (UNLESS (REPORT-RESULT (= (+ 5 6) 11) '(= (+ 5 6) 11)) (SETF #:G2867 NIL))
  (UNLESS (REPORT-RESULT (= (+ -1 -6) -7) '(= (+ -1 -6) -7))
    (SETF #:G2867 NIL))
  #:G2867)

正是这段代码,就在这句话的正上方,不起作用。如果我将它粘贴到 REPL 中,我会收到以下错误(我使用的是 Clozure Common Lisp):

Unbound variable: #:G2867 [Condition of type UNBOUND-VARIABLE]

现在,如果我采用相同的代码,将 gensym 替换为变量名,例如“x”,它就可以正常工作。

那么,我们如何解释以下惊喜:

  1. 调用所有这些的“test-+”宏工作正常。

  2. “combine-results”宏的宏扩展运行。

  3. 如果我从“combine-results”的宏扩展中删除 gensym,它 确实有效。

我唯一可以推测的是,您不能使用包含 gensyms 文字用法的代码。如果是这样,为什么不呢?如何解决这个问题?如果这不是解释,那是什么?

谢谢。

最佳答案

GENSYM 创建非内部符号。当宏正常运行时,这不是问题,因为在整个表达式中都替换了相同的 uninterned 符号。

但是当您将表达式复制并粘贴到 REPL 中时,这不会发生。 #: 告诉读者返回一个未实习的符号。因此,每次出现的 #:G2867 都是一个不同的符号,您会收到未绑定(bind)变量警告。

如果你在执行 MACROEXPAND 之前执行 (setq *print-circle* t) 它将使用 #n=#n#将相同符号链接(symbolic link)在一起的符号。

关于macros - 为什么这个 Lisp 宏作为一个整体可以工作,即使每个部分都不起作用?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/12657481/

相关文章:

lisp - 如何从 Lisp 中的二叉搜索树中删除

common-lisp - 如何从字符串创建关键字符号?

methods - 在 defmethod 中使用 Common Lisp 用户定义类型

c - 错误使用字符串化

macros - 在 Rust 宏 2.0 中使用内部规则

list - 如何用 nil 创建点对

command-line - 编写从命令行执行但不在解释器内部执行的Common Lisp代码

python - 如何在 LibreOffice 中运行 python 宏?

c - 拒绝指针的数组大小的宏

functional-programming - funcall 的语法糖?