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


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://emacs-lisp.ivory.cafe/qiu-zhi/eval.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
