> For the complete documentation index, see [llms.txt](https://emacs-lisp.ivory.cafe/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://emacs-lisp.ivory.cafe/qiu-zhi/eval.md).

# 10.4 Eval（求值）（DONE）

绝大多数情况下，在运行时，表达式会自动进行求值。但在少数情况下，你可能需要写运行时求值的表达式，比如在读取一段文本或从属性列表中获取文本后，你想将这段文本作为代码执行，此时，你使用函数 eval。通常的编码并不需要使用 eval，而应该使用其他函数。举个例子，获取某个变量的值，你应当使用 symbol-value，而不应该使用 eval，即便 eval 也可行。在举个例子，我们可以将表达式储存在属性列表中，然后使用 eval 调用，但是更好的做法是 将表达式转换成函数储存起来，然后使用 funcall 调用。

本节介绍的函数和变量都和表达式求值有关，它们有的指明了求值的限制，有些保存了最近一次的求值记录。加载文件同样设涉及求值(查阅 加载)。

通常将函数储存在数据结构中，使用 funcall 或 apply 调用 会比 将表达式储存在数据结构中然后直接求值 更加简洁灵活。函数提供了一种将信息作为参数传入的能力。

Function: eval form \&optional lexical

这个函数是对表达式求值的最基础函数。它会在当前环境下，对表达式求值，然后返回求值结果。表达式的具体求值方式取决于表达式本身，详情查阅 表达式。

参数 lexical 指明了局部变量语法绑定的规则。如果该参数被省略，或为 nil，这意味着这个表达式使用默认的动态绑定规则。如果为 t，则使用词法绑定。参数 lexical 同样可以为一个非空alist，用来指明语法环境或语法绑定方式；不过这个特性仅在某些特定时候有用，比如在 Emacs Lisp 调试器里。详情查阅语法绑定。

由于 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 限制（见下文）

Command: eval-region start end \&optional stream read-function

这个命令会在当前缓冲区中，从位置 start 读取文本，eval，直到 位置 end，或出现错误。

默认情况下，这个命令并不产生输出。如果参数 stream 为非nil，那么任何由输出函数（详情查阅输出函数）产生的输出，包括过程中的求值结果，都会使用 stream 打印出来。（详情查阅 输出流）

参数 read-function 应为 nil 或函数，用来取代默认的读取函数 read，来读取表达式。这个函数应当接受，读取的流作为其参数。你也可以通过设置 load-read-function 变量来改变输入（详情查阅 程序如何加载），不过使用 read-function 参数会更具鲁棒性。

eval-region 并不会改变位点，而且永远返回 nil。

Command: eval-buffer \&optional buffer-or-name stream filename unibyte print

这个命令和 eval-region 很像，但是提供了一些不同的参数以实现不同特性。eval-buffer 作用在名称为 buffer-or-name 的缓冲区上（查阅 The GNU Emacs  Manual中的变窄章节）。buffer-or-name  可以为一个缓冲区对象，也可以是缓冲区名称（字符串），也可以省略（nil），省略意味着传入当前缓冲区。参数 stream 和 eval-region 中很像。但 stream 为 nil，而 print 为 non-nil 时，求值会被丢弃，但由输出函数产生的输出会返回到回显区。filename 指明用于 load-history 的文件名（查阅 卸载），默认使用 buffer-file-name的值（查阅缓冲区文件名称）。如果参数 unibyte 为非nil，read 会将字符串转换为 unibyte。

eval-current-buffer 是该命令的一个别名。

User Option: max-lisp-eval-depth

这个变量定义了调用 eval，apply 以及 funcall 的最大深度。超过最大深度会抛出 “Lisp nesting exceeds max-lisp-eval-depth" 错误。

这个限制和一个错误联系在一起。这是 Emacs Lisp 检测由病态函数 造成的无限递归的一种手段。如果你讲这个变量的值设置的很大，那么代码可能会抛出 栈溢出 错误。在一些系统中，这种溢出错误可以被处理掉。这种情况下，通常求值器会终止，然后返回顶层命令循环（top-level）。注意，这种情况下没有办法进入 Emacs Lisp 调试器。查阅 错误调试。

这个深度限制与 eval ，apply，和funcall 的使用密切相关，比如 调用函数，递归求值，显式调用等等。

默认值为 800。如果你将这个变量设置为 100 以下的值，Lisp 会在达到数值时，重新将其设置为 100。进入 Lisp 调试器会增加这个值，以确保调试器可以正常运作。

max-specpdl-size 是提供了另一种限制，查阅 局部变量。

Variable: values

这个变量的值是一个列表，其内容为表达式历史：那些曾经由 Emacs 标准命令读取，求值，打印到缓冲区的表达式。（这里不包括 \*ielm\* 缓冲区，也不包括 C-j，C-x，C-e调用的函数，以及 lisp-interaction-mode 中的求值命令）这些记录会按照顺序排列。

```
(setq x 1)
     ⇒ 1
(list 'A (1+ 2) auto-save-default)
     ⇒ (A 3 t)
values
     ⇒ ((A 3 t) 1 …)
```

在查询最近使用的表达式时，这个变量很有用。不过打印这个变量通常不是个好主意，因为它太长了。相反的，你应该检查某些特定的元素，就像下面这样：

```
;; 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
```
