> For the complete documentation index, see [llms.txt](https://ayakaleaf-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://ayakaleaf-pro.ayaka.space/latex/zh-cn/shen-du-wen-zhang/20-how-does-expandafter-work-from-basic-principles-to-exploring-tex-s-source-code.md).

# \expandafter 是如何工作的：从基本原理到探索 TeX 源代码

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

## 引言

现在，我们已经涵盖了全面探讨以下内容所必需的背景主题： `\expandafter`:

* TeX 标记的基础知识，以及它们是如何计算的；
* TeX 展开过程背后的原理；
* TeX 在文档处理期间对临时标记列表的使用和创建；
* TeX 如何使用和“调度”多个输入源（包括临时标记列表）。

本文将把这些主题/概念结合起来，解释 TeX 的 `\expandafter` 命令背后的机制：简言之，即它如何工作。

## 那么，接下来讲 \expandafter

背后的理念 `\expandafter` 是强制在 TeX 通常会进行展开之前，就展开一个命令（标记）。给定两个标记， $$\mathrm{T\_1}$$ 和 $$\mathrm{T\_2}$$ 的作用 `\expandafter` $$\mathrm{T\_1T\_2}$$ 会使 TeX 处理 $$\mathrm{T\_1}\text{<}$$对以下内容的展开 $$\mathrm{T\_2}\text{>}$$，其中 $$\text{<}\dots\text{>}$$ 表示一个标记列表。TeX 会展开 $$\mathrm{T\_2}$$ 提前，以便标记 $$\mathrm{T\_1}$$ （例如原语或宏）能够看到或作用于由以下内容展开所产生的标记 $$\mathrm{T\_2}$$。如果该标记 $$\mathrm{T\_2}$$ 表示不可展开的项，例如非活动字符或（大多数）原语，那么 `\expandafter` 的作用不会改变任何内容：TeX 会继续处理标记 $$\mathrm{T\_1T\_2}$$ 以正常方式。

### \expandafter 使用简介

如果您还没有使用过 `\expandafter`，这里有一个将其与原语结合使用的示例 `\uppercase{...}`。假设我们想要排版主 `.tex` 输入文件的名称，但使用大写字母。我们可能知道以下 TeX 原语命令：

* `\uppercase`：顾名思义，将字符标记转换为其等效的大写形式（如果存在）；
* `\jobname`：如我们所见，会展开以给出主文件的名称 `.tex` 文件中使用的

假设我们的 TeX 文件名为 `mycode.tex` 我们可能会合理地预期 `\uppercase{\jobname}` 会排版出 `MYCODE`。但事实并非如此，它排版出 `mycode` 为小写。哪里“出错”了？

如果我们将以下内容的一般用法 `\uppercase` 写为

```
\uppercase{<token list>}
```

我们可以说 `\uppercase` 会查阅 `<token list>` 并且只会对以下内容执行操作（改变大小写）： *字符标记* 它在其中检测到的 `<token list>`：所有非字符标记都会被 *忽略* 因为 `\uppercase` 不会“查看内部”（展开）非字符标记来了解它们包含或代表什么。由于一个标记只是一个整数值，因此 `\uppercase` 所要做的只是浏览标记列表，检查其中每个标记的数值是否 `<token list>` 落在表示字符标记的数值范围内。顺便一提， `\uppercase` 还会 *改变活动字符的大小写* 以创建一个大写活动字符；由于它仍然是活动的，也需要先被定义，否则 TeX 将生成错误： `未定义的控制序列`，不过我们跑题了……

例如，即使我们定义一个仅包含文本的宏

```
\def\foo{some lower-case text}
```

那么 `\uppercase{\foo}` 仍会排版出 `some lower-case text` 而不是 `SOME LOWER-CASE TEXT` 正如我们所希望的那样，这仅仅是因为 `\uppercase` 的操作并不会试图确定 `\foo` 代表什么：它将 `\foo` 视为一个命令标记并忽略它，正如它对待 `\jobname`.

### 如何修复？\expandafter 来救场

为了排版文件名的大写版本，我们需要修改 `\uppercase{\jobname}` ，方法是强制 TeX 将 `\jobname` 替换为它的展开结果（一串字符标记） *在* `\uppercase` 开始工作之前。再次强调，展开被用于移除 `\jobname` 标记（命令），并将其替换为展开结果（一个包含字符标记的标记列表）。因此，如果我们写成

```
\uppercase\expandafter{\jobname}
```

那么它就能正常工作： `MYCODE` 将会被排版出来。发生的情况是，TeX 开始处理 `\uppercase` 并立即检查必需的左花括号字符（`{`）；但是，TeX 检测到一个 `\expandafter` 命令，这使它暂时“转移注意力”去处理 `\expandafter{\jobname}`.

如果我们比较

`\expandafter` $$\mathrm{T\_1T\_2}$$

与我们的示例

`\expandafter{\jobname}`

我们可以看到

* $$\mathrm{T\_1} =\space$$`{`<sub>标记</sub>
* $$\mathrm{T\_2} =\space$$`\jobname`<sub>标记</sub>

其中 `{`<sub>标记</sub> 和 `\jobname`<sub>标记</sub> 指的是由 TeX 计算出的标记值——下标记法 <sub>标记</sub> 用于提醒我们，TeX 工作在整数标记的世界中。

写成 `\uppercase\expandafter{\jobname}` 之所以有效，是因为概括而言（详情随后说明）， `\expandafter` 会使 TeX 执行以下任务：

1. 读取并保存起始的 `{`<sub>标记</sub>;
2. 读取下一个标记： `\jobname`<sub>标记</sub>。TeX 识别出 `\jobname`<sub>标记</sub> 代表一个可展开命令，并将其展开。 `\jobname`<sub>标记</sub> 被其展开结果替换——一系列字符标记；
3. 在展开 `\jobname` 命令后，TeX 将 `{`$$\_\mathrm{token}$$ “放回输入中”，并使用由以下内容展开而来的标记列表 `\jobname` 以便 TeX 读取 `\uppercase{`<sub>标记</sub>`<\jobname 的展开结果>`<sub>标记列表（字符）</sub>`}`，这就产生了我们所需的结果。

下图展示了 TeX 如何处理 `\uppercase\expandafter{\jobname}`——请从图的底部向上阅读，以跟随处理流程。

![\expandafter 如何工作](/files/ba39039bbd2206787ffd469e2de28e34bb9528ae)

以下说明解释了处理的各个阶段。

1. TeX 开始处理 `\uppercase` 并检查必需的左花括号字符（`{`），但检测到一个 `\expandafter` 该命令。
2. 如果我们比较 `\expandafter` $$\mathrm{T\_1T\_2}$$ 到我们输入的 `\expandafter{\jobname}` 我们可以看到 $$\mathrm{T\_1} =$$`{`<sub>标记</sub> 和 $$\mathrm{T\_2} =$$`\jobname`<sub>标记</sub>。请注意，这里我们将使用下标 <sub>标记</sub> 来表示 TeX 正在处理整数标记值。
3. `\expandafter` 读取，然后临时保存该 `{`<sub>标记</sub> ，方法是将该整数标记值存储在内部变量中。稍后，TeX 会在处理完以下内容后，将该标记重新插入输入中 `\jobname` 该命令。
4. `\expandafter` 读取下一个标记， `\jobname`<sub>标记</sub>，并展开 `\jobname` 该命令。
5. 的展开 `\jobname` 创建一个临时标记列表，其中包含一系列表示该 `.tex` 文件名的字符标记。请注意，由以下内容生成的所有字符标记 `\jobname` 均使用类别码 12 计算。
6. 一旦 `\jobname` 已被展开，TeX 就会重新插入步骤 3 中保存的标记（`{`<sub>标记</sub>），并将其放回输入中。TeX 通过创建另一个包含该 *单个* `{`<sub>标记</sub>
7. TeX 现已完成处理 `\expandafter`，从而得到两个可作为 TeX 输入源使用的标记列表。TeX 现在恢复处理 `\uppercase` ，但已配置其输入，使得由以下内容创建的两个标记列表 `\expandafter` 成为以下内容的标记来源 `\uppercase`——它现在看到 `\uppercase{`<sub>标记</sub>`<\jobname 的展开结果>`<sub>标记列表（字符）</sub>`}`. `\uppercase` 现在看到一系列字符标记，并能够产生我们所需的结果。
8. 在读取由以下内容产生的所有字符标记后 `\jobname`，TeX 恢复从其先前的输入源（我们的 `.tex` 文件）获取标记，并从中读取下一个标记：结束的 `}` ，这是终止由以下内容处理的标记列表所必需的 `\uppercase`.

### \expandafter 与内部标记列表

临时标记列表是 *至关重要的* 组成部分，属于 `\expandafter`的处理行为：理解这些标记列表的使用和存在，有助于阐明 `\expandafter` 如何实现其结果，尤其是在尝试编写或理解使用多个连续 `\expandafter` 命令以实现更复杂标记处理形式的宏时： `\expandafter\expandafter\expandafter...`

另一个关键要素是 `\expandafter`的行为，特别是在使用多个连续 `\expandafter` 命令时，是对以下内容的使用： *递归* （在 TeX 软件本身内部）——我们将在本文稍后讨论这一主题。

为了进一步帮助我们理解临时标记列表，我们将再看一个示例： `\expandafter`，这次配合 `\the` 该命令。

#### \expandafter 与内部标记列表：示例 2

在本示例中，我们将看到 `\expandafter` 如何通过以下方式影响存储在标记寄存器中的标记： `\toks` 命令。以下是我们将使用的 TeX 原语：

* `**\count** *寄存器*=*数字*`：用于存储数值的 TeX 原语 `*数字*` 到 TeX 位置 `*寄存器*`;
* `**\toks** *寄存器*={*标记列表*}`：用于存储以下内容的 TeX 原语 `*标记列表*` 到标记寄存器位置中 `*寄存器*`——保存一系列标记供日后使用；
* `**\the** *标记*`：一个可展开的 TeX 原语命令，用于处理 `*标记*`，尽管具体结果取决于该 `*标记*` 正在处理的内容的性质。 `\the` 有多种用途：其中之一是排版存储在 TeX 参数或变量（例如寄存器）中的值。其他用途 `\the` 包括插入存储在标记寄存器中的标记副本。这里，我们将使用 `\the` 来排版存储在一个 `\count` 寄存器中的值。

我们将从以下 TeX 代码开始，将该值存储 `12345` 在 TeX 的 `\count` 寄存器中 `99`:

```
\count99=12345
```

如果我们想排版存储在以下位置的值 `\count99` 我们可以使用 `\the\count99` （或者 `\number\count99`).

接下来，我们将使用 `\toks` 命令将一些标记存储到标记寄存器中 `99`:

```
\toks99={\the\count99 }
```

存储在标记寄存器中的标记列表 `99` 将包含以下内容：

|             |                                                              |
| ----------- | ------------------------------------------------------------ |
| **TeX 标记值** | **所表示的项目**                                                   |
| 5382        | `\the`                                                       |
| 7885        | `\count`                                                     |
| 3129        | `9` （字符码 57，类别码 12），得到的标记值为 $$256 \times 12 + 57 = 3129$$    |
| 3129        | `9` （字符码 57，类别码 12），得到的标记值为 $$256 \times 12 + 57 = 3129$$    |
| 2592        | `<空格>` （字符码 32，类别码 10），得到的标记值为 $$256 \times 10 + 32 = 2592$$ |

请注意，由以下内容创建的标记列表 `\toks99` 确实 *不* 包含实际存储在以下位置的数据值 `\count99` 因为 `\toks` 命令不执行展开：它仅创建标记并存储它们。在我们的示例中， `\the` 没有被展开，因此它不会处理 `\count99`；这里 `\the` 仅被转换为一个标记（值为 5382）并存储在标记列表中。

如果我们希望 `\toks99` 标记列表包含表示存储在以下位置数据的标记 `\count99` 我们将需要某种方法来创建这些标记（使其可用），以便 `\toks` 命令能够访问它们。当然， `\expandafter` 可以为我们做到这一点。如果我们写：

```
        \toks99=\expandafter{\the\count99 }
```

对以下内容的动作/处理 `\toks` 命令将在以下期间被“搁置” `\expandafter` 导致（强制）展开 `\the` ，后者继而作用于 `\count` 以生成一个临时标记列表，该列表包含表示存储在以下位置数据的字符标记 `\count99`。一个虽小但重要的点是 `<空格>` 数字后的字符 `99`：该 `<空格>` 字符用于终止 TeX 在搜索数值时的扫描过程。

这里，以下内容的作用 `\expandafter` 与 `\jobname` 示例非常相似。

1. 读取并保存起始的 `{`<sub>标记</sub>.
2. 读取下一个标记， `\the`<sub>标记</sub>，它代表一个可展开命令，因此 TeX 将其展开。 `\expandafter` 强制展开 `\the` ，后者随即作用于 `\count99` 以将存储在以下位置的数据转换 `\count` 寄存器中 `99` （数字 12345）为一个临时标记列表。该列表将包含表示数字的字符标记 `1`, `2`, `3`, `4` 和 `5`——类别码为 12 的字符标记。
3. 在展开和处理完 `\the`后，TeX 将 `{`<sub>标记</sub> “放回输入中”，并使用由以下内容产生的标记列表 `\the\count99` 以便 TeX 读取 `\toks99={`<sub>标记</sub>`<\the\count99 的展开结果>`<sub>标记列表（字符）</sub>`}` ，这就产生了我们所需的结果。

下图总结了这一事件序列——请从图的底部向上阅读，以跟随处理流程。

![\expandafter 如何工作](/files/0187f3a7f9c4f3d6470322fecfe451ea81a2f832)

1. TeX 开始处理 `\toks`；它看到可选的 `=` 符号，然后检查必需的左花括号字符（`{`，或任何类别码为 1 的字符），用于表示标记列表的开始。然而，TeX 检测到一个 `\expandafter` 命令，并转而执行该命令。
2. 如果我们比较 `\expandafter` $$\mathrm{T\_1T\_2}$$ 到我们输入的 `\expandafter{\the\count99 }` 我们可以看到 $$\mathrm{T\_1} =$$ `{`<sub>标记</sub> 和 $$\mathrm{T\_2} =$$ `\the<sub>token</sub>`.
3. `\expandafter` 读取，然后临时保存该 `{`<sub>标记</sub> （TeX 会将该整数标记值临时存储在内部变量中）。稍后，TeX 会在处理完以下内容后，将该标记重新插入输入中 `\the`
4. `\expandafter` 读取下一个标记， `\the`<sub>标记</sub> 并将其展开。
5. 的展开 `\the` 通过处理以下内容创建一个临时标记列表 `\count99`——该标记列表包含一系列字符标记，它们表示存储在该 `\count` 寄存器中 `99`.
6. 一旦 `\the` 已被展开，TeX 就会重新插入步骤 3 中保存的标记（`{`<sub>标记</sub>）中的数据值，并将该标记放回输入中。TeX 通过创建另一个包含该标记的列表来做到这一点 *单个* 标记 `{`<sub>标记</sub>.
7. TeX 现已完成处理 `\expandafter` 并生成两个标记列表，准备作为下一个输入源使用。TeX 恢复处理 `\toks99=` 但现在 TeX 已配置其输入，使得由以下内容创建的两个标记列表 `\expandafter` 成为以下内容的标记来源 `\toks`——它现在看到 `{`<sub>标记</sub>`<\the\count99 的展开结果>`<sub>标记列表（字符）</sub>`}`. `\toks` 现在可以访问并存储表示数据值（`12345`）的 5 个字符标记序列，该数据值存储在 `\count99`中：这就是我们所需的结果。
8. 在读取由以下内容产生的所有字符标记后 `\the\count99`，TeX 恢复从其先前的输入源（我们的 `.tex` 文件）获取标记，并从中读取下一个标记：结束的 `}` ，这是终止要由以下内容保存的标记列表所必需的 `\toks99={...}`.

## \expandafter 的实际工作方式

在本节中，我们将以“低层次”的视角查看 TeX 内部：探索 TeX 中实现以下行为的源代码/函数 `\expandafter`。细节以伪 C 代码表示，但任何熟悉其他编程语言的人都应该能够理解。

下方带注释的图示解释了 TeX 如何实现 `\expandafter` ，作为一个名为 `expand()`的更大函数的一部分——这是驱动 TeX 展开处理的核心函数。在负责实现以下内容的部分中 `\expandafter` 我们可以看到 [*递归* 行为](https://en.wikipedia.org/wiki/Recursion) ，其中对该 `expand()` 函数的另一次调用用于处理读入的第二个标记， $$\mathrm{T\_2}$$，适用于以下情况： $$\mathrm{T\_2}$$ 是可展开的。

尽管这段代码出现在 Knuth 的 TeX 引擎中，但该图概述的基本原则适用于所有 TeX 引擎。

![\expandafter 在 TeX 内部如何工作](/files/a5be8c0d92ad87416e92f7f31d577dad0075defd)

的首要任务 `expand()` 是确定要展开的命令是宏还是原语，因为宏具有专门的展开过程，由一个名为以下内容的函数处理 `macrocall()`.

如果要展开的命令是原语，则 `expand()` 函数使用当前命令代码值（存储在全局变量 `curcmd`中）来识别需要处理的具体原语。我们可以在更完整的以下列表中看到这些细节： `expand()`:

```
    void expand(void)
    {
    //curcmd 是一个全局变量
    if(curcmd != macro) // curcmd < 111
    {
      switch(curcmd)
      {
        case \expandafter: // 处理 \expandafter T1T2 命令
        {
            gettoken(); // 读取标记 T1
            t = curtok; // 将标记 T1 保存在局部变量 t 中
            gettoken(); // 读取标记 T2
            if(curcmd > 100) // 标记 T2 是否可展开？
                expand();    // 是！T2 是可展开的：
                             // 通过以下方式对 T2 执行展开
                             // 对 expand() 进行递归函数调用
            else
                backinput(); // T2 不可展开：把该标记
                             // 放回输入中，稍后再次读取

            curtok = t ;  // 将全局变量 curtok 恢复为已保存的 T1 值
            backinput() ; // 将标记 T1 放回输入中
                          // 位于由 T2 展开产生的标记之前
        }
        break;

        // 处理其他可展开命令的代码
        case “convert to text” command: // 以下任一命令：\number、\string、\romannumeral、
                                        // \meaning、\fontname、\jobname
                                        // 它们共享相同的 curcmd 值
        break;

        case \noexpand: // 抑制下一个标记的展开
        ...
        break;

        case \csname:  // 构造一个控制序列名称。
        ...
        break;

        case \the: // 插入一些标记
        ....
        break;

        case “\if... test command” : // 处理 TeX 的条件语句之一：
                                      // \if, \ifcat, \ifnum, \ifdim,\ifodd, \ifvmode,
                                      // \ifhmode, \ifmmode, \ifinner, \ifvoid,
                                      // \ifhbox, \ifvbox, \ifx, \ifeof, \iftrue, \iffalse,
                                      // \ifcase, \ifdefined, \ifcsname, \iffontchar
        ...
        break;

        case “\fi or \else”: // 终止当前条件判断
        ...
        break;

        // 等等，适用于 TeX 引擎支持的任何其他可展开原始命令
        // TeX 引擎

        }

    }else // 不是可展开的原始命令：它是一个宏
        {
             macrocall()
        }
        //... 更多代码已移除
    }
```

### TeX 对全局变量的偏爱

也许是反映了它的年代和设计所处的时期，TeX 的源代码大量使用了所谓的 [全局变量](https://en.wikipedia.org/wiki/Global_variable)——事实上，这样的变量有成百上千个。就其本质而言，全局变量可以在 TeX 源代码的任何地方被更改/修改——而对 Knuth 的 TeX 来说，这是一份单一的巨型文件，包含超过 25,000 行代码和数百个函数。理解 TeX 的工作方式并不总是一件容易的事……

为了处理 `\expandafter`，TeX 使用一个名为 `gettoken()` 的函数从当前输入中读取标记；该函数的作用是创建一个标记，并设置 TeX 源代码中广泛使用的几个关键全局变量的值。由 `gettoken()`的作用更新的两个变量，在 `\expandafter`:

* `curtok`： （当前标记）刚刚读入的标记的整数值；
* `curcmd`： （当前命令码）该标记所代表的命令（或字符）的命令码 `curtok`.

在处理 `\expandafter`$$\mathrm{T\_1T\_2}$$ 时，TeX 读取标记 $$\mathrm{T\_1}$$ 并将其值（一个整数）临时保存在一个名为 `t`的局部变量中。然后 TeX 读取 $$\mathrm{T\_2}$$ 并检查该标记是否表示一个可展开命令——通过检查它的命令码（`curcmd`）是否大于 100。若是，TeX 需要展开由 $$\mathrm{T\_2}$$ 表示的命令，并再次调用函数 `expand()`：这就是一个 *递归* 因为 `expand()` 函数调用自身的例子。对递归展开性质有所了解，尤其是在使用 `\expandafter`时，有助于理解多个连续的 `\expandafter` 命令——即 `\expandafter\expandafter\expandafter...` 是如何实现其效果的。

如果标记 $$\mathrm{T\_2}$$ 是可展开的，展开就会发生；当对 `expand()` 的递归调用返回时， `\expandafter` 实现中的代码会把标记 $$\mathrm{T\_1}$$ 重新插回输入中。全局变量 `curtok` 会被重新赋值为已保存标记的值——存储在局部变量 `t`中，也就是标记 $$\mathrm{T\_1}$$的值——然后调用函数 `backinput()。`

#### 函数 backinput()

顾名思义，这个函数会把一个标记“放回输入中”。为此，TeX 使用全局变量 `curtok` 的当前值创建一个只包含单个标记的标记列表（其整数值由 `curtok`提供）。TeX 还会安排其输入处理，以确保该单标记列表在适当的时候会被 TeX 作为后续输入处理的一部分重新读取。请注意，标记 $$\mathrm{T\_1}$$ 是在 *之后重新插入的*，这确保 TeX 会在 *在* 读取由 $$\mathrm{T\_2}$$.

### 处理宏：macrocall() 函数

如前所述，所有宏，以及某些原始命令，都是可展开的，所有展开处理都通过 `expand()` 函数进行。不过， `expand()` 会谨慎地使用 `curcmd` （当前命令）值来区分可展开原始命令与宏，因为宏展开过程由一个名为 `macrocall()`的专用函数处理。宏需要一种专门的展开过程，因为宏参数和分隔标记必须以非常特定且严格的方式进行扫描；因此，这一过程被委派给一个专门负责此事的函数： `macrocall()`.

#### 宏展开与宏执行

宏 *展开* 并不等同于宏 *执行*：宏的展开是 TeX 在让宏 *准备好执行之前所进行的预执行过程*。宏的“执行”发生在 TeX 正在主动读取和处理该宏定义（替换文本）及其参数（实参）中所包含的标记时。

#### 宏展开

为了展开一个宏，TeX 首先检查该宏是否接受参数；如果是， `macrocall()` 就会非常仔细地扫描输入，寻找将要成为该宏参数的标记。这个过程包括检查用户输入中是否存在宏原始定义中使用的任何分隔标记——宏调用中的标记模式必须与存储定义中包含的标记模式完全一致。不过，被用作分隔符的标记会被 TeX 直接丢弃：实际上，它们只是 TeX 用来确定真正将成为宏参数的标记的一种“标点符号”——也就是用户希望由宏处理的标记。有关分隔标记的更多信息，请参见 [TeX 宏究竟是如何工作的](/latex/zh-cn/geng-duo-zhu-ti/22-how-tex-macros-actually-work-part-4.md).

对于宏原始定义中存在的每个参数（`#1, #2...#9`），TeX 都会扫描实际的宏调用，以确定用户提供的哪些标记将用于每个参数（即构成宏的参数）。该过程会生成一个或多个迷你标记列表：每个宏参数对应一个。

一旦检测到任何宏参数并准备好它们的标记列表，TeX 就会从内存中取出存储的宏定义（替换文本），并安排其输入处理，使得每当 TeX 准备读取/处理更多标记时，它都会从宏的替换文本中读取这些标记，从而执行该宏。在适当的时候，在宏执行过程中，表示宏参数的标记列表将被送入宏替换文本中的正确位置。

再一次，宏命令的展开意味着 *移除* 该宏命令（标记）从输入中，并且 *将其替换为* 存储为宏替换文本的标记列表。

如需深入了解 TeX 的宏处理，请参阅六部分文章系列 [TeX 宏究竟是如何工作的？](/latex/zh-cn/geng-duo-zhu-ti/01-a-six-part-series-how-do-tex-macros-actually-work.md)

[第 1 部分](/latex/zh-cn/shen-du-wen-zhang/19-how-does-expandafter-work-an-introduction-to-tex-tokens.md) [第 2 部分](/latex/zh-cn/shen-du-wen-zhang/22-how-does-expandafter-work-the-meaning-of-expansion.md) [第 3 部分](/latex/zh-cn/shen-du-wen-zhang/21-how-does-expandafter-work-tex-uses-temporary-token-lists.md) [第 4 部分](/latex/zh-cn/shen-du-wen-zhang/20-how-does-expandafter-work-from-basic-principles-to-exploring-tex-s-source-code.md) [第 5 部分](/latex/zh-cn/shen-du-wen-zhang/17-how-does-expandafter-work-a-detailed-macro-case-study.md) [第 6 部分](/latex/zh-cn/shen-du-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://ayakaleaf-pro.ayaka.space/latex/zh-cn/shen-du-wen-zhang/20-how-does-expandafter-work-from-basic-principles-to-exploring-tex-s-source-code.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.
