【问题标题】:Evaluation of passed parameters inside macro body评估宏体内传递的参数
【发布时间】:2013-02-27 02:44:03
【问题描述】:

我对如何评估传递给宏的参数有疑问,详情如下。

定义了这个宏

(defmacro test-macro (xlist)
    `(* ,@xlist))

还有这个全局变量(defvar *test-list* '(1 100 2 200))

*test-list*被传递给这个宏(test-macro *test-list*)时,会返回这个错误——

value *TEST-LIST* is not of the expected type LIST.
   [Condition of type TYPE-ERROR]

但是如果函数修改成这个,则返回列表

(defmacro test-macro (xlist)
    `(,@xlist)) ;; removed the * operator

(test-macro *test-list*) 将返回 (1 100 2 200)

所以我怀疑为什么,@xlist 在第一种情况下没有得到评估,即在应用* 运算符时。非常感谢任何帮助。

【问题讨论】:

    标签: macros lisp common-lisp


    【解决方案1】:

    调试宏时,正确的方法是使用macroexpand,而不是评估宏形式。例如,在您的情况下:

    (defmacro test-macro1 (xlist) `(* ,@xlist))
    (macroexpand '(test-macro1 foo))
    ==> (* . FOO)
    (defmacro test-macro2 (xlist) `(,@xlist))
    (macroexpand '(test-macro2 foo))
    ==> FOO
    

    这两者都不是你想要的。

    【讨论】:

    • @5fists 在 slime 中有一个宏展开的热键;这是 ,1 用于 slimv
    【解决方案2】:

    令人困惑的是宏是一个预处理器:它没有知道运行时值的内置机制。所以当你使用这个词时:

    (test-macro test-list)
    

    宏看到的只是标识符test-list:它预先不知道运行时值是一个列表,只知道源程序使用了这个变量标识符。

    宏是源到源的重写器:它不了解程序的动态。更聪明的编译器可能会看到 test-list 是一个常量并进行内联,但宏扩展器就没有那么聪明了。

    你能做的大概是这样的:

    (defmacro test-macro (xlist)
      (cond
        (;; If we see test-macro is being used with a quoted list of things
         ;; then we can rewrite that statically.
         (and (pair? xlist)
              (eq? (car xlist) 'quote)
              (list? (cadr xlist)))
         `(list 'case-1 (* ,@(cadr xlist))))
    
        (;; Also, if we see test-macro is being used with "(list ...)"
         ;; then we can rewrite that statically.
         (and (pair? xlist)
              (eq? (car xlist) 'list))
         `(list 'case-2 (* ,@(cdr xlist))))
    
        (else
         ;; Otherwise, do the most generic thing:
         `(list 'case-3 (apply * ,xlist)))))
    
    
    
    ;; This hits the first case:
    (test-macro '(3 4 5))
    
    ;; ... the second case:
    (test-macro (list 5 6 7))
    
    ;; ... and the third case:
    (defvar test-list '(1 100 2 200))
    (test-macro test-list)
    

    关于您的第二个版本:宏:

    (defmacro test-macro (xlist)
      `(,@xlist))
    

    相当于:

    (defmacro test-macro (xlist)
      xlist)
    

    这就是为什么您没有收到在第一个版本中收到的错误。

    【讨论】:

    • 感谢您如此详细地解释它。您在代码中使用的某些功能在我正在使用的 Common Lisp 实现中不可用。不能说我完全理解您的代码,但有几个要点对我进一步学习 Lisp 很有帮助。看来我还有很长的路要走:)。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-11-14
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-10-21
    • 1970-01-01
    相关资源
    最近更新 更多