Lisp 中的反引号扩展

Backquote expansion in Lisp

我是 Lisp 初学者,我很难理解为什么下面的代码会给我一个错误。

(dolist (elem '(mapcar
                mapcon))
  (when (fboundp `',elem) (print "hello")))

谢谢。

编辑: 更多上下文。我在 Elisp 中写了以下内容,但我不知道如何修复它。

(dolist (ui-elem '(menu-bar-mode
                   tool-bar-mode
                   tooltip-mode
                   scroll-bar-mode
                   horizontal-scroll-bar-mode))
  (when (fboundp `',ui-elem) (ui-elem -1)))

备注

在您的问题中,您混合了 common-lispelisp,但它们是两种不同的语言。然而,这个问题涉及两种语言中相同的概念。

需要引用符号

您要编写的代码检查符号是否绑定到函数。 您可能已经知道的是,您可以在符号上调用 fboundp 来确定这一点:

(fboundp 'menu-bar-mode)
=> t

当你对上面的形式求值时,'menu-bar-mode(quote menu-bar-mode)一样,被求值作为符号对象menu-bar-mode。这是作为 fboundp.

参数给出的值

在您的示例中,您想要遍历符号列表,对其调用 fboundp 并在符号表示函数时调用该函数。您可以按如下方式执行此操作:

(dolist (s '(menu-bar-mode and other symbols))
  (when (fboundp s)
    (funcall s -1)))

符号列表 '(menu-bar-mode and other symbols) 被引用,这意味着当 dolist 计算它时,它看到一个符号列表。 s 在循环的每次迭代中绑定到的值是一个符号对象,不需要引用它们。

引用符号是您在代码中编写它们时必须做的事情,这样它们就不会被解释为变量。当您遍历符号列表时,您已经在操作符号。

另请注意,Common Lisp 和 Emacs Lisp 都是“Lisp-2”,这意味着您必须使用 (funcall ui-elem -1) 而不是写 (ui-elem -1)。当您编写后一种形式时,这意味着按字面意思调用名为 ui-elem 的函数,因为对于函数应用程序,列表中的第一个符号不会被计算,而是按字面意思使用。

引用层数过多

我在执行你的代码时遇到的实际错误是:

(wrong-type-argument symbolp 'mapcar)

它可能看起来像 'mapcar 表示一个符号,因为当 希望解释器 评估 一些代码作为符号,你需要引用它。但是,Lisp 打印机以一种可以 read 返回“相似”对象的方式写入对象。如果我希望符号是数字,则打印的错误消息如下,其中符号 foo 未加引号打印:

(+ 'foo 3)

;; error: (wrong-type-argument number-or-marker-p foo)

在您的错误消息中,您尝试用作符号的形式是 (quote mapcar)。回想一下,当您直接调用 fboundp:

(fboundp 'mapcar)

这和你写的一样:

(fboundp (quote mapcar))

首先,(quote mapcar) 被计算为符号 mapcar。然后,fboundp 应用于该值。

但是当你写下面的时候,while ui-elem 绑定到符号 mapcar:

(fboundp `',ui-elem)

这相当于:

(fboundp `(quote ,ui-elem))

fboundp 的参数被计算(quote mapcar)。您有一个额外的报价级别。你可以这样写:

(fboundp `,ui-elem)

不过这样的话,就不用backquote/comma了,直接写:

(fboundp ui-elem)