Macros 宏体内部传递参数的求值

Macros 宏体内部传递参数的求值,macros,lisp,common-lisp,Macros,Lisp,Common Lisp,我怀疑传递给宏的参数是如何计算的,详情如下 此宏已定义 (defmacro test-macro (xlist) `(* ,@xlist)) 还有一个全局变量(defvar*testlist*”(1100 2200)) 将*测试列表*传递到此宏(测试宏*测试列表*)时,将返回此错误- value *TEST-LIST* is not of the expected type LIST. [Condition of type TYPE-ERROR] 但如果函数被修改为此,则返回列

我怀疑传递给宏的参数是如何计算的,详情如下

此宏已定义

(defmacro test-macro (xlist)
    `(* ,@xlist))
还有一个全局变量
(defvar*testlist*”(1100 2200))

*测试列表*
传递到此宏
(测试宏*测试列表*)
时,将返回此错误-

value *TEST-LIST* is not of the expected type LIST.
   [Condition of type TYPE-ERROR]
但如果函数被修改为此,则返回列表

(defmacro test-macro (xlist)
    `(,@xlist)) ;; removed the * operator
(测试宏*测试列表*)
将返回
(1100 2200)


因此,我的疑问是为什么在第一种情况下,@xlist没有得到评估,即当应用
*
运算符时。非常感谢您的帮助。

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

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

这两者都不是您想要的。

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

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

这两者都不是您想要的。

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

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

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

混淆之处在于宏是一个预处理器:它没有内置机制来了解运行时值。因此,当您使用该术语时:

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

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

感谢您对其进行如此详细的解释。您在代码中使用的一些函数在我正在使用的通用Lisp实现中不可用。我不能说我完全理解您的代码,但我得到了一些要点,这些要点将对我进一步学习Lisp非常有帮助。看来我还有很长的路要走:)。谢谢你解释得这么详细。您在代码中使用的一些函数在我正在使用的通用Lisp实现中不可用。我不能说我完全理解您的代码,但我得到了一些要点,这些要点将对我进一步学习Lisp非常有帮助。看来我还有很长的路要走:)@5fists有一个热键可以在黏液中进行宏观扩张;是的,1美元slimv@5fists粘液中有一个宏扩展热键;是的,1比1