为了让你在不小心操作时不必担心,我们现在向 Lisp 解释器输入一个会生成错误信息的命令。 这一操作是无害的;事实上,我们经常会有意触发错误信息。 一旦你理解了相关术语,错误信息其实很有参考价值。 与其称其为 “错误(error)” 信息,不如称之为 “帮助(help)” 信息。 它们就像陌生国度里给旅行者指引方向的路标;读懂它们可能有些困难,但一旦理解,就能指明前进的方向。
错误信息由内置的 GNU Emacs 调试器生成。我们会进入调试器。
输入 q 即可退出调试器。
我们要做的是对一个未加引号、且首元素不是有效命令的列表进行求值。 下面这个列表与我们刚才使用的几乎完全相同,只是前面没有单引号。 将光标移到它的正后方,然后输入 C-x C-e:
(this is an unquoted list)
一个 *Backtrace* 窗口会打开,你应该能在其中看到如下内容:
---------- Buffer: *Backtrace* ---------- Debugger entered--Lisp error: (void-function this) (this is an unquoted list) eval((this is an unquoted list) nil) elisp--eval-last-sexp(nil) eval-last-sexp(nil) funcall-interactively(eval-last-sexp nil) call-interactively(eval-last-sexp nil nil) command-execute(eval-last-sexp) ---------- Buffer: *Backtrace* ----------
光标会出现在这个窗口中(可能需要等待几秒才会显示)。 要退出调试器并关闭调试窗口,请输入:
q
请现在就输入 q,让自己确信可以顺利退出调试器。 然后再次输入 C-x C-e 重新进入。
根据我们已经掌握的知识,基本可以读懂这条错误信息。
*Backtrace* 缓冲区需要从下往上阅读;它会告诉你 Emacs 执行了哪些操作。
当你输入 C-x C-e 时,你是在交互式调用 eval-last-sexp 命令。
eval 是“evaluate(求值)”的缩写,sexp 是 “symbolic expression(符号表达式)” 的缩写。
该命令的含义是 “对光标前的最后一个符号表达式求值”。
上面的每一行都会告诉你 Lisp 解释器接下来求值的内容。 最近执行的操作位于最上方。 该缓冲区被称为 *Backtrace* 缓冲区,是因为它可以让你回溯 Emacs 的执行过程。
在 *Backtrace* 缓冲区的顶部,你会看到这一行:
Debugger entered--Lisp error: (void-function this)
Lisp 解释器尝试对列表的第一个原子 ‘this’ 进行求值。 正是这一操作触发了错误信息 ‘void-function this’。
该信息包含 ‘void-function’ 和 ‘this’ 两个部分。
‘function’ 一词我们之前已经提到过。这是一个非常重要的概念。 就本节而言,我们可以这样定义:函数 是一组告诉计算机执行特定操作的指令。
现在我们可以理解这条错误信息了:‘void-function this’。 该函数(即 ‘this’ 这个词)没有任何可供计算机执行的指令定义。
略显特殊的 ‘void-function’ 这一说法,是为了贴合 Emacs Lisp 的实现方式: 当一个符号没有附加函数定义时,本该存放指令的位置就是空的。
另一方面,我们之前通过求值 (+ 2 2) 成功计算了 2 加 2,
由此可以推断,符号 + 一定包含一组计算机可执行的指令,
这些指令的作用就是对 + 后面的数字进行加法运算。
在这类情况下,其实可以阻止 Emacs 进入调试器。 我们这里不讲解具体方法,但会说明出现的结果, 因为你在使用存在问题的 Emacs 代码时可能会遇到类似情况。 这种情况下,你只会在回显区看到一行错误信息,样式如下:
Symbol's function definition is void: this
只要按下任意按键,哪怕只是移动光标,这条信息就会消失。
我们知道 ‘Symbol’ 的含义。它指的是列表的第一个原子,也就是 ‘this’。 ‘function’ 指的是告诉计算机执行操作的指令。 (严格来说,符号是告诉计算机去哪里查找指令,不过这一细节我们暂时可以忽略。)
这条错误信息的含义可以理解为:‘Symbol's function definition is void: this’。 该符号(即 ‘this’ 一词)缺少可供计算机执行的指令。