Macros 在宏中取消引用时未定义变量

Macros 在宏中取消引用时未定义变量,macros,lisp,common-lisp,Macros,Lisp,Common Lisp,以下是我的宏定义: *(defmacro run-test (test) `(format t "Run test ~a ... ~a" ',test ,test)) *(run-test (= 1 1)) Run test (= 1 1) ... T NIL 目前一切正常,因此我定义了第二个宏(用于运行多个测试): 这个结果不是我想要的,我想要用sexp替换tc的每个值,并在运行测试中评估该值。我试着换线 do (run-test tc) 与 但这表明了一个错误

以下是我的宏定义:

*(defmacro run-test (test)
   `(format t "Run test ~a ... ~a" ',test ,test))
*(run-test (= 1 1))
Run test (= 1 1) ... T
NIL
目前一切正常,因此我定义了第二个宏(用于运行多个测试):

这个结果不是我想要的,我想要用sexp替换
tc
的每个值,并在运行测试中评估该值。我试着换线

          do (run-test tc)

但这表明了一个错误

未定义变量:TC


如何更改此选项以获得正确的结果?

查看扩展,例如
(运行测试(=1))

如您所见,代码尝试调用
(运行测试tc)
。但是
runtest
在表单上运行;当您传递包含表单的变量时,它将不起作用

如果将代码更改为
(runtest,tc)
,则试图在宏扩展期间引用
tc
变量,但它仅在运行时绑定

一种解决方案是在宏观扩张时做更多的事情:


只是作为练习。您可以删除宏:

(defun run-test (test)
  (format t "~%Run test ~a ... ~a" test (eval test)))

(defun run-tests (tests)
  (mapc 'run-test tests)
  (values))

* (run-tests '((= 2 (1+ 1))
               (= 1 1)))

我喜欢使用
循环
构建宏扩展的可能解决方案。通常,手工编写代码(至少一次)是一种非常有用的方法,该代码是宏应该扩展到的所需代码。在这种情况下,它应该是
(progn(runtestform1)(runtestform2)…
,从这个角度来看,
循环显然是获得它的一个好方法。非常好的解决方案。我再次查看了LISP宏的详细信息。我对宏扩展阶段和运行时阶段的理解是错误的。是的,事实上,作为Python播放器,编写函数对我来说很简单。这只是为了让我学习LISP的宏。无论如何,谢谢。@WuLi永远不要使用宏,函数就足够了。如果你绝对想这样做,你可能想考虑身体会返回什么,如果它是一个函数(并且嵌套,这适用于你想要扩展发生的站点的确切形式,不管变量绑定发生了什么,宏都是源代码转换)。@ Vatine:是的,“永远不要在函数足够的地方使用宏”是一条很好的经验法则,但“eval是邪恶的”也是如此“。我认为宏是完成此任务的好方法,如果操作正确,它的优点是不需要引用参数。但是
eval
方法确实更容易正确,而且因为测试函数是在开发过程中使用的,而不是在程序的实际运行时使用的,所以在这里更容易接受。
          do (run-test ,tc)
(loop for tc in '((= 1 1)) do (run-test tc))
(defmacro run-tests (&body body)
  `(progn ,@(loop for tc in body collect `(run-test ,tc))))
(defun run-test (test)
  (format t "~%Run test ~a ... ~a" test (eval test)))

(defun run-tests (tests)
  (mapc 'run-test tests)
  (values))

* (run-tests '((= 2 (1+ 1))
               (= 1 1)))