通常情况下,表达式会因为出现在运行中的程序里而自动被求值。只有在少数场景下,你才需要编写代码,对运行时才计算出来的表达式进行求值 ——比如从正在编辑的文本中读取一个表达式,或是从属性列表中获取一个表达式。这时就可以使用 eval 函数。
很多时候其实并不需要使用 eval,应该改用其他方式:例如,要获取变量的值,虽然 eval 也能用,但更推荐用 symbol-value;与其把表达式存在属性列表里,之后再用 eval 求值,不如直接存储函数,再通过 funcall 调用。
本节介绍的函数和变量,可用于对表达式求值、为求值过程设定限制,或记录最近的返回值。加载文件本身也会执行求值(see 加载)。
通常来说,更清晰、更灵活的做法是:把函数存放到数据结构中,再用 funcall 或 apply 调用,而不是把表达式存进去再用 eval 求值。使用函数可以方便地以参数形式向其传递信息。
这是用于对表达式进行求值的基础函数。它会在当前环境中对 form 进行求值,并返回求值结果。form 对象的类型决定了它的求值方式。See 形式的种类。
参数 lexical 用于指定局部变量的作用域规则(see Scoping 变量绑定的作用域规则):
若值为 t,表示使用词法作用域对 form 求值(这是推荐使用的值);
若省略该参数或值为 nil,则使用旧的「仅动态作用域」规则。
lexical 的值也可以是一个非空列表,用于为词法绑定指定特定的 词法环境(lexical environment);不过该特性仅适用于特殊场景(例如 Emacs Lisp 调试器)。列表中的每个成员要么是一个表示「词法符号 - 值对」的 cons 单元,要么是一个符号(代表某个特殊变量,该变量若被绑定则会使用动态作用域)。
由于 eval 本身是一个函数,因此调用 eval 时传入的参数表达式会被求值两次:第一次是在调用 eval 之前的参数准备阶段,第二次则是 eval 函数自身对该表达式的求值。示例如下:
(setq foo 'bar)
⇒ bar
(setq bar 'baz)
⇒ baz
;; Here eval receives argument foo
(eval 'foo)
⇒ bar
;; Here eval receives argument bar, which is the value of foo
(eval foo)
⇒ baz
当前对 eval 的活跃调用层数被限制为 max-lisp-eval-depth(见下文)。
该函数会对当前缓冲区中,由位置 start 和 end 界定的区域内的表达式进行求值。它会从该区域中读取表达式,并逐个调用 eval 对其求值,直至到达区域末尾,或触发未被处理的错误为止。
默认情况下,eval-region 不会产生任何输出。但如果 stream 参数的值非 nil,那么输出函数(see 输出函数)产生的所有输出,以及对该区域内表达式求值得到的结果,都会通过 stream 进行打印。See 输出流。
若 read-function 参数的值非 nil,则它必须是一个函数 ——该函数会替代 read,用于逐个读取表达式。调用此函数时会传入一个参数(即用于读取输入的流)。你也可以通过变量 load-read-function(see 程序的加载方式)来指定这个读取函数,但使用 read-function 参数的方式更健壮。
eval-region 不会移动光标位置(point),该函数始终返回 nil。
该函数与 eval-region 功能类似,但参数提供了不同的可选特性。eval-buffer 会对缓冲区 buffer-or-name 的整个可访问区域进行操作(see Narrowing in The GNU Emacs Manual)。buffer-or-name 可以是一个缓冲区对象、缓冲区名称(字符串类型),也可以是 nil(或省略该参数)—— 这种情况下会使用当前缓冲区。
stream 参数的用法与 eval-region 中一致,除非 stream 为 nil 且 print 非 nil:此时,表达式求值产生的结果仍会被丢弃,但输出函数的输出内容会打印在回显区中。
filename 是用于 load-history 的文件名(see 卸载),默认值为 buffer-file-name(see Buffer File Name)。若 unibyte 非 nil,则 read 函数会尽可能将字符串转换为单字节编码。
该变量用于定义对 eval、apply 和 funcall 的调用所允许的最大嵌套深度,超出则会抛出错误(错误信息为 "Lisp nesting exceeds max-lisp-eval-depth")。
设置这一限制并在超限时抛出错误,是 Emacs Lisp 用于避免因函数定义不当而导致无限递归的机制。如果把 max-lisp-eval-depth 的值调得过大,这类代码反而可能引发栈溢出。在部分系统上,这种溢出可以被处理:此时正常的 Lisp 求值会被中断,控制流返回到顶层命令循环(top-level)。注意:这种情况下无法进入 Emacs Lisp 调试器。See 发生错误时进入调试器。
深度计数包含 eval、apply、funcall 的内部使用(例如调用 Lisp 表达式中提到的函数、递归求值函数参数与函数体),也包含 Lisp 代码中显式的直接调用。
该变量的默认值为 1600。如果将其设为小于 100 的值,当达到该值时 Lisp 会自动将其重置为 100。
为了能够调试无限递归错误,在进入 Lisp 调试器时,如果剩余深度空间很小,Emacs 会临时增大 max-lisp-eval-depth 的值,以确保调试器自身有足够的空间运行。在运行 handler-bind 的异常处理器时也会执行同样的操作。See 编写处理错误的代码。
变量 lisp-eval-depth-reserve 限制了 Emacs 在这些特殊场景下,可以给 max-lisp-eval-depth 额外增加的深度上限。
该变量的默认值为 200。
该变量的值是一个列表,保存了由 Emacs 标准命令从缓冲区(包括小缓冲区)中读取、求值并打印的所有表达式的返回值。(注意:这 不包括 在 *ielm* 缓冲区中的求值,也不包括在 lisp-interaction-mode 下使用 C-j、C-x C-e 等类似求值命令的求值结果。)
该变量已被废弃,会在未来版本中移除,因为它会持续增大 Emacs 进程的内存占用。因此我们不建议使用它。
values 中的元素按最新返回的在前排列。
(setq x 1)
⇒ 1
(list 'A (1+ 2) auto-save-default)
⇒ (A 3 t)
values
⇒ ((A 3 t) 1 ...)
这个变量可用于回溯最近求值过的表达式的结果。
通常不建议直接打印 values 本身,因为它可能非常长。你应该像下面这样,只查看其中特定的元素:
;; Refer to the most recent evaluation result.
(nth 0 values)
⇒ (A 3 t)
;; That put a new element on, ;; so all elements move back one. (nth 1 values) ⇒ (A 3 t)
;; This gets the element that was next-to-most-recent ;; before this example. (nth 3 values) ⇒ 1