> For the complete documentation index, see [llms.txt](https://overleaf-pro.ayaka.space/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://overleaf-pro.ayaka.space/latex/zh-cn/shen-ru-wen-zhang/21-how-does-expandafter-work-tex-uses-temporary-token-lists.md).

# \expandafter 是如何工作的：TeX 使用临时标记列表

[第1部分](/latex/zh-cn/shen-ru-wen-zhang/19-how-does-expandafter-work-an-introduction-to-tex-tokens.md) [第2部分](/latex/zh-cn/shen-ru-wen-zhang/22-how-does-expandafter-work-the-meaning-of-expansion.md) [第3部分](/latex/zh-cn/shen-ru-wen-zhang/21-how-does-expandafter-work-tex-uses-temporary-token-lists.md) [第4部分](/latex/zh-cn/shen-ru-wen-zhang/20-how-does-expandafter-work-from-basic-principles-to-exploring-tex-s-source-code.md) [第5部分](/latex/zh-cn/shen-ru-wen-zhang/17-how-does-expandafter-work-a-detailed-macro-case-study.md) [第6部分](/latex/zh-cn/shen-ru-wen-zhang/18-how-does-expandafter-work-a-detailed-study-of-consecutive-expandafter-commands.md)

## 展开与内部记号列表

到目前为止，我们已经探讨了记号、记号列表以及 TeX 展开概念背后的核心原理。在本节中，我们将使用 TeX 原语 `\\jobname` 来介绍展开处理的一个重要方面：TeX 对 *临时记号列表*——它们是一个 *基础* 运作方式的一个方面， `\\expandafter` 正如我们将在本系列文章后文中看到的那样。

`\\jobname` 是一个可展开的 TeX 原语，其展开会生成一系列字符记号，用以表示主输入 `.tex` 文件的名称。例如，假设我们在一个 `.tex` 名为 `mycode.tex`:

```
    我的文件名是 \jobname .tex %注意 \jobname 后面的空格
```

这将排版为

```
    我的文件名是 mycode.tex
```

当我们使用 `\\jobname` 命令时，所得的排版字符并非从你的物理 `.tex` 文件中读取，那么它们来自哪里：TeX 将这些记号存储/读取在何处？对于用户而言不可见（即在 TeX 本身的深处），针对 `\\jobname` 的展开过程会创建一个临时记号列表，该列表由表示你的文件名的一系列字符记号构成。一旦 `\\jobname` 创建了该记号列表，TeX 就会暂时将“目光”从当前输入源（此处是我们的 `.tex` 文件）转向从该临时记号列表中读取记号（字符记号）。当 TeX 需要另一个输入记号时，它会从该内部列表中读取下一个记号，并持续如此，直到抵达列表末尾；此时，TeX 会恢复从先前的输入源读取记号，而这里该输入源将是从我们的 `.tex` 输入文件中读取的文本。

如下图所示，TeX 会恢复读取 `.tex` 输入文件，并且位置恰好是它在处理 `\\jobname`之后停止的位置——读取空格字符之后、读取“.”字符之前。实际上，句点（.）正等待从 TeX 的输入缓冲区读取——这是 TeX 内存中的一小块区域，用于保存从 `.tex` 文件读取的一行文本——TeX 每次读取并处理你的 `.tex` 文件中的一行，而不会将整个文件读入内存。

阅读下图时，请从底部开始向上看，以跟随处理流程。

![TeX 如何展开 \jobname](/files/dbd99ec16d3b153eaab1f7edf209df158072096f)

回顾我们对展开的讨论，可以看到， `\\jobname` 的展开导致 `\\jobname` 命令（记号）被 *移除* 从输入中移除，并 *替换* 为展开产生的记号：为保存该名称而生成的临时记号列表 `.tex` 文件中。

可展开命令（例如 `\\jobname`）并不是唯一会“秘密地”创建并使用记号列表来实现其效果的 TeX 原语。例如，命令 `\\uppercase` 和 `\lowercase` 都会创建内部记号列表来改变其参数的大小写。大小写转换完成后，TeX 会切换为从这些命令生成的记号列表中读取字符记号。除了将数据写入物理磁盘文件外，记号列表是 TeX 唯一的“记号数据存储”机制。

### 记号的来源：TeX 是一位杂耍大师

当 TeX 处理一份典型文档时，它必须管理 *许多* 个记号来源：来自众多物理磁盘文件的输入，以及处理期间创建的无数内部记号列表。在本节中，我们将非常简要地探讨 TeX 如何“杂耍般地”管理这些输入源。

假设我们想要一个简单的宏，用于排版我们的 `.tex` 文件中：

```
    \def\myfile{我的文件名是 \jobname .tex}
```

之后，在我们的 `.tex` 文件中的某处，我们调用该宏 `\myfile`：TeX 会暂时从通过你的文本（`.tex`）文件创建/读取记号，切换到从其内存中存储的 `\myfile` 定义（记号列表）读取记号。当 TeX 执行该 `\myfile` 宏（处理其记号）时，它将检测到一个表示 `\\jobname` 命令的记号，其展开会创建另一个临时记号列表，TeX 必须从中读取记号。即使在这个简单场景中，TeX 也必须管理三个输入源：

1. 该 `.tex` 包含该 `\myfile` 宏的文本文件；
2. 存储该定义的记号列表 `\myfile` 宏的文本文件；
3. 由 `\\jobname` 命令在该 `\myfile` 宏内创建的记号列表。

当 TeX 处理文档时，它会不断在输入源——物理文件和记号列表——之间切换，那么 TeX 如何跟踪这一切？答案是，TeX 引擎在内部维护了一个所谓的 [输入栈](https://en.wikipedia.org/wiki/Stack_\(abstract_data_type\)) ，它充当一种“记忆”，使 TeX 能够在输入源之间切换时记住自己正在做什么（正在从何处读取）。

不深入过多细节，TeX 引擎内部的代码使用一个名为 `curinput` （当前输入）的全局变量，除其他作用外，它会告诉 TeX 当前是在从物理文件还是记号列表读取。 `curinput` 还会向 TeX 指明应从何处（当前记号列表或其文本缓冲区中）获取下一个记号。如果 TeX 正从记号列表读取， `curinput` 还会记录正在处理的是哪种类型的记号列表——例如，作为宏存储的记号列表，或这些记号是否来自其他来源。

在需要时， `curinput` 变量将被更改为指向新的输入源，而 TeX 当前的“输入状态”（来源和位置）将被保存在输入栈中，以便 TeX 稍后能返回该确切位置（某个 `.tex` 文件中的位置，或记号列表中的下一个记号）。一旦该新输入源耗尽（例如记号列表中不再有记号，或到达文件末尾），它就会从栈中弹出，而 `curinput` 会被更新，以确保 TeX 恢复从先前的来源获取记号。

## 深入探究（可选阅读）

以下各节为喜欢了解细节的读者提供额外的背景信息。

### 真实的记号列表

下图使用 Overleaf 定制构建的 Knuth TeX 生成，该版本可访问 TeX 的内部数据和数据结构。这个记号列表示意图以先前给出的简化版本为基础，并包含额外数据，例如显示由 `\\jobname` 生成的字符具有类别码 12，而非通常的类别码 11。在此图中，“节点”只是 TeX 对所使用的一个内存存储单元的名称。

![TeX 记号列表内部](/files/085b8a2c0fdea49d2170592ac5c9f2a7877e272a)

### TeX 如何读取和处理 \jobname

此外，为求完整，以下概述了 TeX 在检测到 `\\jobname` 位于我们输入 `.tex` 文件中时的“思考过程”。在此图中，我们看到 TeX 如何检测转义字符（`\` ，其类别码为 0），处理字符序列 `jobname`，生成一个记号并查找该 `\\jobname` 命令的含义；此时 TeX 会发现它的命令码大于 100，表明它是一个可展开命令。

![TeX 如何扫描并处理 \jobname](/files/f8767592bc6a809a160faba3734389744f643af6)

[第1部分](/latex/zh-cn/shen-ru-wen-zhang/19-how-does-expandafter-work-an-introduction-to-tex-tokens.md) [第2部分](/latex/zh-cn/shen-ru-wen-zhang/22-how-does-expandafter-work-the-meaning-of-expansion.md) [第3部分](/latex/zh-cn/shen-ru-wen-zhang/21-how-does-expandafter-work-tex-uses-temporary-token-lists.md) [第4部分](/latex/zh-cn/shen-ru-wen-zhang/20-how-does-expandafter-work-from-basic-principles-to-exploring-tex-s-source-code.md) [第5部分](/latex/zh-cn/shen-ru-wen-zhang/17-how-does-expandafter-work-a-detailed-macro-case-study.md) [第6部分](/latex/zh-cn/shen-ru-wen-zhang/18-how-does-expandafter-work-a-detailed-study-of-consecutive-expandafter-commands.md)


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## 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, and the optional `goal` query parameter:

```
GET https://overleaf-pro.ayaka.space/latex/zh-cn/shen-ru-wen-zhang/21-how-does-expandafter-work-tex-uses-temporary-token-lists.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

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.
