将列表传递给Common Lisp中的宏

ore*_*uro 2 lisp macros metaprogramming common-lisp quote

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

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

(gen '("foo" "bar"))
Run Code Online (Sandbox Code Playgroud)

产生的错误是:

***-DEFUN / DEFMACRO:QUOTE是一种特殊的运算符,不能重新定义。可以使用以下重新启动:ABORT:R1
中止主循环

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

令我更加困惑的是,下面的代码(有关答案在此处退出)的运行正常。

(defmacro easy-one (str-lst)
  `(mapc #'(lambda (str) (print str)) ,str-lst))
(easy-one '("foo" "bar"))
Run Code Online (Sandbox Code Playgroud)

Bar*_*mar 7

不要引用列表。宏不评估其参数,因此您不需要像对普通函数那样引用它们以防止对其进行评估。

(gen ("foo" "bar"))
Run Code Online (Sandbox Code Playgroud)

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

(get (quote ("foo" "bar")))
Run Code Online (Sandbox Code Playgroud)

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

(defun quote () (print "foo"))
Run Code Online (Sandbox Code Playgroud)

这就是为什么您在尝试重新定义内置命令时会收到错误消息QUOTE

第二个示例的区别在于,您只是将参数替换为扩展,而不是在扩展代码中使用其值。因此它扩展为

(mapc #'(lambda (str) (print str)) '("foo" "bar")))
Run Code Online (Sandbox Code Playgroud)

在此列表将在扩展运行时使用,而不是在宏扩展时使用。需要在其中将其引号,以防止将其作为函数调用进行评估。

您应该使用它macroexpand来查看调试时宏的扩展方式。

  • 你不能那样做。宏在编译时扩展,该变量直到运行时才存在。 (2认同)
  • 如果要在运行时定义一个函数,可以使用`eval`:```((eval`(gen,str-list))``` (2认同)