1

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

这个宏定义

(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在第一种情况下没有得到评估,即在*应用运算符时。非常感谢任何帮助。

4

2 回答 2

4

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

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

两者都不是你想要的。

于 2013-02-27T04:01:29.830 回答
3

令人困惑的是宏是一个预处理器:它没有知道运行时值的内置机制。因此,当您使用该术语时:

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

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

于 2013-02-27T04:08:36.113 回答