将列表传递给 Common Lisp 中的宏

pass a list to macro in Common Lisp

我在将列表传递给宏时遇到问题,该列表将用于生成函数名称。例如,下面的代码会导致错误。

(defmacro gen (str-lst)
  `(defun ,(intern (string-upcase (car str-lst))) () (print "foo")))

(gen '("foo" "bar"))

产生的错误是:

*** - DEFUN/DEFMACRO: QUOTE is a special operator and may not be redefined. The following restarts are available: ABORT :R1
Abort main loop

我应该如何修改我的代码,我的代码有什么问题?

让我更加困惑的是下面的代码,关于哪个答案存在here,工作正常。

(defmacro easy-one (str-lst)
  `(mapc #'(lambda (str) (print str)) ,str-lst))
(easy-one '("foo" "bar"))

不要引用列表。宏不会计算它们的参数,因此您不需要引用它们来防止它们被计算,就像您对普通函数所做的那样。

(gen ("foo" "bar"))

当你引用它时,你正在执行

(get (quote ("foo" "bar")))

str-list的值是列表(quote ("foo" "bar")),所以(car str-list)是符号QUOTE。结果,宏扩展为

(defun quote () (print "foo"))

这就是为什么您会收到一条错误消息,抱怨您正在尝试重新定义 built-in QUOTE

第二个示例的不同之处在于,您只是将参数替换到扩展中,而不是在扩展代码中使用它的值。所以它扩展为

(mapc #'(lambda (str) (print str)) '("foo" "bar")))

此处将在展开运行时使用列表,而不是在展开宏时使用。它需要在那里被引用,以防止它被评估为函数调用。

您应该使用 macroexpand 来查看您的宏在调试时是如何展开的。