【问题标题】:Undefined variable while unquoting in macro在宏中取消引用时未定义的变量
【发布时间】:2013-10-15 04:07:12
【问题描述】:

这是我的宏定义:

*(defmacro run-test (test)
   `(format t "Run test ~a ... ~a" ',test ,test))
*(run-test (= 1 1))
Run test (= 1 1) ... T
NIL

现在一切正常,所以我定义了第二个宏(运行多个测试):

*(defmacro run-tests (&body body) 
   `(loop for tc in ',body 
      do (run-test tc)))
* (run-tests (= 2 (1+ 1)) (= 1 1))
Run test TC ... (= 2 (1+ 1) Run test TC ... (= 1 1)

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

          do (run-test tc)

          do (run-test ,tc)

但这表示错误

未定义变量:TC

我怎样才能改变它以获得正确的结果?

【问题讨论】:

    标签: macros lisp common-lisp


    【解决方案1】:

    看看 e.g. 的扩展(run-tests (= 1 1)):

    (loop for tc in '((= 1 1)) do (run-test tc))
    

    如您所见,代码尝试调用(run-test tc)。但是run-test 在表单上运行;当你传递一个变量包含一个表单时它不会工作。

    如果您将代码更改为 (run-test ,tc),您将尝试在宏扩展期间引用 tc 变量,但它仅在运行时绑定。

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

    (defmacro run-tests (&body body)
      `(progn ,@(loop for tc in body collect `(run-test ,tc))))
    

    【讨论】:

    • 我喜欢使用loop 构造宏扩展的可能解决方案。它通常是一种有用的方法,至少一次手动编写宏应扩展为的所需代码。在这种情况下,它应该是 (progn (run-test form1) (run-test form2) ...),从这个角度来看,loop 显然是获得它的好方法。
    • 非常好的解决方案。我再次查看了 LISP 宏的详细信息。我对宏扩展阶段和运行时阶段的理解是错误的。
    【解决方案2】:

    就像一个练习。您可以摆脱宏:

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

    【讨论】:

    • 是的,实际上,作为一名 Python 玩家,编写函数对我来说很简单。只是为了我学习LISP的宏。无论如何,谢谢。
    • @WuLi 永远不要使用宏,函数就足够了。如果您绝对想这样做,您可能需要考虑主体会返回什么,它是否是一个函数(对于嵌套,这适用于您希望发生扩展的站点的确切形式,忽略发生了什么变量绑定要生效,宏毕竟是源代码转换)。
    • @Vatine:是的,“永远不要在函数足够的地方使用宏”是一个很好的经验法则,但“eval 是邪恶的”也是如此。我认为宏是完成这项任务的好方法,如果正确完成,并且具有不需要在参数上引用的优点。但确实,eval 方法更容易正确使用,而且由于测试函数是在开发过程中使用的,而不是在程序的实际运行时使用,所以这里更容易接受。
    猜你喜欢
    • 2019-08-26
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-05-21
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-01-01
    相关资源
    最近更新 更多