不清楚您要做什么,尽管几乎可以肯定您对某事感到困惑。特别是如果您在宏扩展中调用eval,那么在几乎所有 情况下,您正在做一些严重错误和严重危险的事情。我从来没有想过我想要宏扩展到包括eval 在内的东西,而且我已经编写 Lisp 很长时间了。
话虽如此,下面是您如何调用与宏关联的函数,以及为什么它很少是您想要做的。
宏只是其域和范围是源代码的函数:它们是从一种语言到另一种语言的编译器。完全可以调用与宏关联的函数,但该函数将返回源代码,然后您需要对该源代码进行评估。如果您想要一个处理不是源代码的运行时 data 的函数,那么您需要该函数,并且您不能通过某种魔术将宏转换为该函数,这似乎是你想做什么:那个魔术不存在,也不可能存在。
例如,如果我有一个宏
(defmacro with-x (&body forms)
`(let ((x 1))
,@forms))
然后我可以在一点源代码上调用它的宏函数:
> (funcall (macro-function 'with-x)
'(with-x (print "foo")) nil)
(let ((x 1)) (print "foo"))
但是这样做的结果是另外一点源代码:我需要编译或评估它,我无能为力。
实际上在(几乎?)所有情况下,这与macroexpand-1 相同):
> (macroexpand-1 '(with-x (print "foo")))
(let ((x 1)) (print "foo"))
t
你大概可以把macroexpand-1写成macro-function:
(defun macroexpand-1/equivalent (form &optional (env nil))
(if (and (consp form)
(symbolp (first form))
(macro-function (first form)))
(values (funcall (macro-function (first form)) form env)
t)
(values form nil)))
那么,如果调用宏的结果是源代码,你会如何处理该源代码以获得不是源代码的结果?好吧,你必须评估它。然后,好吧,既然评估器无论如何都会为你扩展宏,你不妨写一些类似
(defun evaluate-with-x (code)
(funcall (compile nil `(lambda ()
(with-x ,@code)))))
所以在任何情况下您都不需要调用宏的函数。这并不是将宏转换为处理非源代码数据的函数的魔术:这是一个完全由爆炸部分组成的可怕恐怖。
看起来这个问题可能起源于this one,而潜在的问题是这不是 CL-WHO 所做的。尤其是认为像 CL-WHO 这样的东西是一种用于获取某种列表并将其转换为 HTML 的工具是一种混淆。它不是:它是一种获取基于 CL 的语言的源代码的工具,但它包括一种表达与 CL 代码混合的 HTML 输出的方式,并将其编译成可以做同样事情的 CL 代码。恰好是 CL 源代码表示为列表和符号的情况,但 CL-WHO 并不是真的:它是一个从“CL-WHO 语言”到 CL 的编译器。
那么,让我们试试我们上面尝试过的技巧,看看为什么它是一场灾难:
(defun form->html/insane (form)
(funcall
(compile nil `(lambda ()
(with-html-output-to-string (,(make-symbol "O"))
,@form)))))
如果你不仔细看的话,你可能会认为这个函数确实起到了神奇的作用:
> (form->html/insane '(:p ((:a :href "foo") "the foo")))
"<p></p><a href='foo'>the foo</a>"
但事实并非如此。如果我们在这个完全无害的列表上调用 form->html/insane 会发生什么:
(:p (uiop/run-program:run-program "rm -rf $HOME" :output t))
提示:如果您没有很好的备份,请不要在此列表中致电 form->html/insane。
CL-WHO 是一种编程语言的实现,它是 CL 的严格超集:如果您尝试将其转换为将列表转换为 HTML 的函数,您最终会得到涉及您每次修补的相同核武器的东西你打电话给eval,除了核武器藏在一个锁着的柜子里,你看不到它。但它并不关心这一点:如果你把它点燃,它仍然会将几英里内的所有东西都变成放射性灰烬和瓦砾。
因此,如果您想要一个将列表(不是源代码的列表)转换为 HTML 的工具,请编写该工具。 CL-WHO 在实现过程中可能有这种工具的胆量,但您不能按原样使用它。
当你试图以这种方式滥用宏时,你会遇到同样的问题:调用宏的函数的结果是 Lisp 源代码,并评估你需要的源代码 @987654339 @ 或 eval 的等效项。而eval 不仅不是解决几乎所有问题的糟糕解决方案:它还是核武器。对于某些问题,核武器可能是很好的解决方案,但它们很少而且相差甚远。