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-lisp
和 elisp
,但它们是两种不同的语言。然而,这个问题涉及两种语言中相同的概念。
需要引用符号
您要编写的代码检查符号是否绑定到函数。
您可能已经知道的是,您可以在符号上调用 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)
我是 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-lisp
和 elisp
,但它们是两种不同的语言。然而,这个问题涉及两种语言中相同的概念。
需要引用符号
您要编写的代码检查符号是否绑定到函数。
您可能已经知道的是,您可以在符号上调用 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)