我通过Practical Common Lisp阅读/工作。我在关于在Lisp中构建测试框架的章节。
我有功能"测试 - +"实现如下,它的工作原理:
(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逐步宏扩展它们(使用ctrl-c RET,映射到macroexpand-1)。
所以,"检查"呼叫"测试 - +"扩展到这个:
(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 [UNBOUND-VARIABLE类型的条件]
现在,如果我使用相同的代码,将gensym替换为变量名称,例如" x",它可以正常工作。
那么,我们如何解释以下惊喜:
"测试 - +"宏调用所有这些,工作正常。
"结合 - 结果"的宏观扩展宏不运行。
如果我从" combine-results"的宏扩展中移除gensym,它 工作。
我唯一可以推测的是你不能使用代码包含gensyms的文字用法。如果是这样,为什么不,以及如何解决这个问题呢?如果那不是解释,那是什么?
感谢。
答案 0 :(得分:11)
代码在打印和回读后不再是相同的代码。特别是,打印表示中的#:G2867
的两个实例将作为两个分开的符号(尽管共享相同的名称)读回,而它们在原始内部表示中应该相同。
尝试将*PRINT-CIRCLE*
设置为T
,以便在宏展开代码的打印表示中保留标识。
答案 1 :(得分:11)
GENSYM
创建未加密的符号。当宏运行正常时,这不是问题,因为在整个表达式中替换了相同的未处理符号。
但是当您将表达式复制并粘贴到REPL中时,这不会发生。 #:
告诉读者返回一个未加工的符号。因此,每次出现的#:G2867
都是不同的符号,您将获得未绑定的变量警告。
如果您在执行MACROEXPAND之前执行(setq *print-circle* t)
,则会使用#n=
和#n#
表示法将相同的符号链接在一起。