Macros 为什么这个Lisp宏作为一个整体可以工作,即使每个部分都不';不行?

Macros 为什么这个Lisp宏作为一个整体可以工作,即使每个部分都不';不行?,macros,lisp,common-lisp,gensym,Macros,Lisp,Common Lisp,Gensym,我正在阅读/使用实用的通用Lisp。我正在读关于用Lisp构建测试框架的一章 我实现了下面的函数“test-+”,它可以工作: (defun test-+ () (check (= (+ 1 2) 3) (= (+ 5 6) 11) (= (+ -1 -6) -7))) 记住,我说过,它是有效的,这就是为什么接下来的事情如此令人困惑的原因 下面是一些“test-+”所指的代码: (defmacro check (&body forms) `(combin

我正在阅读/使用实用的通用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)))
(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)
然后该宏展开为:

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

未绑定变量:#:G2867[未绑定变量类型的条件]

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

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

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

  • “合并结果”宏的宏展开不运行

  • 如果我从“合并结果”的宏展开中删除gensym,它 确实有效

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


    谢谢。

    在打印并读回后,代码不再是相同的代码。特别是,印刷表示法中的两个
    #:G2867
    实例将作为两个分离的符号(尽管名称相同)读回,而它们在原始内部表示法中应相同


    尝试将
    *PRINT-CIRCLE*
    设置为
    T
    ,以保留宏扩展代码的打印表示形式中的标识。

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

    但是,当您将表达式复制并粘贴到REPL中时,不会发生这种情况<代码>#:告诉读者返回一个不需要的符号。因此,每次出现的
    #:G2867
    都是一个不同的符号,您将收到未绑定变量警告


    如果在进行宏扩展之前使用
    (setq*print circle*t)
    ,它将使用
    #n=
    #n#
    符号将相同的符号链接在一起。

    有一节“让过Lambda”讨论了这种行为,以及为什么gensyms以这种方式工作,以及它与变量捕获的关系等。这里: