我对如何评估传递给宏的参数有疑问,详见下文。
定义了这个宏
(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
在第一种情况下没有被评估,即应用*
运算符时。任何帮助都非常感谢。
答案 0 :(得分:4)
调试宏时,正确的方法是使用macroexpand
,而不是评估宏窗体。例如,在您的情况下:
(defmacro test-macro1 (xlist) `(* ,@xlist))
(macroexpand '(test-macro1 foo))
==> (* . FOO)
(defmacro test-macro2 (xlist) `(,@xlist))
(macroexpand '(test-macro2 foo))
==> FOO
也不是你想要的。
答案 1 :(得分:3)
令人困惑的是,宏是预处理器:它没有内置机制来了解运行时值。所以当你使用术语时:
(test-macro 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)
这就是为什么你没有收到你在第一个版本中收到的错误。