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