> 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/18-how-does-expandafter-work-a-detailed-study-of-consecutive-expandafter-commands.md).

# \expandafter 是如何工作的：连续 \expandafter 命令的详细研究

&#x20;[第 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)&#x20;

## 案例研究：理解 \\\expandafter\\\expandafter\\\expandafter...

高级宏，例如 LaTeX 宏包中的那些，通常会大量使用连续的多个 `\expandafter` 命令来执行复杂的标记“玩弄”。对我们大多数人来说，这样的宏可能很难理解，也很难编写。在本节中，我们将回顾 TeX 处理连续 `\expandafter` 命令：

```
\\expandafter\\expandafter\\expandafter...
```

为了帮助说明，我们将为每个 `\expandafter`——以表明我们指的是哪一个：

```
        \\expandafter1\\expandafter2\\expandafter3...
```

此外，我们将扩展由每个 `\expandafter` 处理的标记的记号，改用 $$\mathrm{T^i\_1}$$ 和 $$\mathrm{T^i\_2}$$ 来表示这些标记 $$\mathrm{T\_1}$$ 和 $$\mathrm{T\_2}$$ 被 `\expandafter` 读入，带有下标 `i`: `\expandafter`<sub>i</sub> $$\mathrm{T^i\_1T^i\_2}$$。我们还将假定有两个标记 $$\mathrm{T\_X}$$ 和 $$\mathrm{T\_Y}$$ 位于 `\\expandafter<sub>3</sub>` 之后，这样我们的输入看起来如下： `\\expandafter<sub>1</sub>\\expandafter<sub>2</sub>\\expandafter<sub>3</sub>` $$\mathrm{T\_{X}T\_{Y}}$$.

当 TeX 开始处理这段输入时，对于 `\\expandafter<sub>1</sub>` 它会看到

* $$\mathrm{T^1\_1} =$$`\\expandafter<sub>2</sub>` 它被保存以供稍后 *重新插入* 回输入中
* $$\mathrm{T^1\_2} =$$`\\expandafter<sub>3</sub>`，而它会被展开

如果我们回顾前面对 [TeX 内部代码](/latex/zh-cn/shen-du-wen-zhang/20-how-does-expandafter-work-from-basic-principles-to-exploring-tex-s-source-code.md) 实现 `\expandafter`，正是通过对 `\\expandafter<sub>3</sub>` 的展开，我们看到了递归的发生。为了处理 `\\expandafter<sub>1</sub>` TeX 已经调用了其内部函数 `expand()`，因此为了处理（展开） `\\expandafter<sub>3</sub>` TeX 正在进行一次 *第二次* 对 `expand()`的调用——来自 `expand()` 函数本身内部。

对于 `\\expandafter<sub>3</sub>` 我们有

* $$\mathrm{T^3\_1 = T\_X}$$，它被保存以供稍后 *重新插入* 回输入中
* $$\mathrm{T^3\_2 = T\_Y}$$，我们将假设它是可展开的

我们进一步假设 $$\mathrm{T\_Y}$$ 的展开产生如下标记序列： $$\mathrm{{T^1\_Y}{T^2\_Y}{T^3\_Y}}\cdots\mathrm{T^N\_Y}$$。我们现在已经到达由命令序列开始的展开过程的末尾 `\\expandafter<sub>1</sub>\\expandafter<sub>2</sub>\\expandafter<sub>3</sub>` $$\mathrm{T\_{X}T\_{Y}}$$ ，TeX 接着开始“展开”由……开始的递归过程 `\\expandafter<sub>1</sub>`.

在处理完 `\\expandafter<sub>3</sub>` TeX 会在其内存中保留一个标记列表，其中包含来自……展开的标记 $$\mathrm{T\_Y\text{: }{T^1\_Y}{T^2\_Y}{T^3\_Y}}\cdots\mathrm{T^N\_Y}$$。TeX 现在开始重新插入它在处理……时保存的标记 `\expandafter` 命令：

1. TeX 首先重新插入标记 $$\mathrm{T\_X}$$ 由 `\\expandafter<sub>3</sub>`. $$\mathrm{T\_X}$$ 被重新插入 *到前面* ，位于……的展开结果之前 $$\mathrm{T\_Y}$$，从而得到一个标记序列： $$\mathrm{{{T\_X}T^1\_Y}{T^2\_Y}{T^3\_Y}}\cdots\mathrm{T^N\_Y}$$.
2. 不过，我们仍需完成由……开始的过程 `\\expandafter<sub>1</sub>` ，它保存了表示……的标记 `\\expandafter<sub>2</sub>`
3. TeX 组装好的最终标记序列，准备在 TeX 处理的下一阶段读取如下： `\\expandafter<sub>2 (token)</sub>` $$\mathrm{T\_{X}T^1\_{Y}T^2\_{Y}T^3\_{Y}\cdots T^N\_Y}$$
4. TeX 现在已经完成了“第一轮”处理，并切换为读取它所生成的标记列表序列——该序列以 `\\expandafter<sub>2 (token)</sub>` 开头，TeX 接着处理它。对于 `\\expandafter<sub>2</sub>` 我们有
   * $$\mathrm{T^2\_1} =\ \mathrm{T\_X}$$ 它被保存以供稍后 *重新插入* 回输入中
   * $$\mathrm{T^2\_2} = \mathrm{T^1\_Y}$$ 它是由……展开产生的第一个标记 $$\mathrm{T\_Y}$$；如果可展开，则继续展开
5. 如果我们假定标记 $$\mathrm{T^1\_Y}$$，即由……展开得到的第一个标记 $$\mathrm{T\_Y}$$展开为 $$\mathrm{{T^A\_{Y1}}{T^B\_{Y1}}{T^C\_{Y1}}}$$ 那么，在 TeX 重新插入 $$\mathrm{T\_X}$$之后，TeX 重新处理得到的标记序列将是： $$\mathrm{{T\_X}{T^A\_{Y1}}{T^B\_{Y1}}{T^C\_{Y1}}{T^2\_Y}{T^3\_Y}\cdots{T^N\_Y}}$$

   我们可以将其重新表述为

   $$\mathrm{T\_X}\text{\<expansion of the first token in }\mathrm{T\_Y}\text{>\<remaining tokens in }\mathrm{T\_Y}\text{>}$$

下图说明了由 TeX 代码

```
    \\expandafter1\\expandafter2\\expandafter3TXTY
```

![显示 TeX 处理多个 \\\expandafter 命令的图像](/files/7fafa79f1cff4b8953f2d6acd5901a65bbb0e015)

### 理论联系实践

举例来说，我们将定义以下宏作为 $$\mathrm{T\_X}$$ 和 $$\mathrm{T\_Y}$$

* $$\mathrm{T\_X}=$$`\foo` 其中我们定义 `\foo` 为 `\\def\\foo#1{\\textbf{#1}}`
* $$\mathrm{T\_Y}=$$`\\bar` 其中我们首先定义 `\\def\\abc{Hello}`, `\\def\\xyz{, World!}` 然后 `\\def\\bar{\\abc\\xyz}`

我们将使用以下代码片段来演示前面的分析：

\\\expandafter\\\expandafter\\\expandafter\\\foo\\\bar

根据我们的讨论， `\\expandafter<sub>1</sub>\\expandafter<sub>2</sub>\\expandafter<sub>3</sub>` $$\mathrm{T\_X}\mathrm{T\_Y}$$ 的结果会产生如下形式的标记序列：

$$\mathrm{T\_X}\text{\<expansion of the first token in }\mathrm{T\_Y}\text{>\<remaining tokens in }\mathrm{T\_Y}\text{>}$$

其确切序列取决于标记 $$\mathrm{T\_Y}$$的性质。如果我们将示例命令代入 $$\mathrm{T\_X}=$$`\foo` 和 $$\mathrm{T\_Y}=$$`\\bar`，其定义为 `\\def\\bar{\\abc\\xyz}`，我们会看到：

* 在……中的第一个标记 $$\mathrm{T\_Y}$$ 是 `\\abc` 及其展开是一个字符标记序列： `Hello`
* ……中的其余标记 $$\mathrm{T\_Y}$$ 是表示 `\\xyz`.

如果我们将这些信息代入我们的“分析”，会得到

$$\begin{align\*} &\mathrm{T\_X}\text{\<expansion of the first token in }\mathrm{T\_Y}\text{>\<remaining tokens in }\mathrm{T\_Y}\text{>}\ &=\text{foo}*{\text{token}}\text{\<expansion of \abc>}*\text{token list (characters)}\text{xyx}*\text{token}\ &=\text{foo}*\text{token}\text{Hello}*\text{token list (characters)}\text{xyx}*\text{token}\\\[10pt] \end{align\*}$$

请注意，下标 <sub>记号</sub> 和 <sub>标记列表（字符）</sub> 用于强调（提醒我们）TeX 读取的是 *整数标记值*，而不是 *文本字符*，因此没有必要在 `\foo`之后显示任何空格字符或其他分隔符：这些分隔符早已被处理或丢弃；在这里，我们确实处于 TeX 的内部世界——标记列表和整数标记值。

当 TeX 处理由我们的 `\expandafter` 命令所生成的内容时，它将排版 `**H**ello, World!`——只有 `H` 被加粗排版。我们可以通过编写等价的 TeX 代码来实现相同结果 `\\foo Hello\\xyz`。请注意， `\foo` 使用了一个参数；因此， `\foo` 会吸收单个 `H` 字符标记作为其参数，并将剩余的字符标记（`ello`）保持不变。

**注意：**

* 编写 `\\foo\\bar` 会产生截然不同的输出：表示 `\\bar` 的标记将被用作 `\foo` 的参数 **`Hello, World`**——所有内容都以粗体排版。
* 编写 `\\expandafter\\foo\\bar` 会使 `\\bar` 被展开，从而产生两个标记： $$\text{abc}*\text{token}\text{xyz}*\text{token}$$。然后，在 $$\text{foo}*\text{token}$$ 被 `\expandafter` TeX 处理标记序列 $$\text{foo}*\text{token}\text{abc}*\text{token}\text{xyz}*\text{token}$$ 从而排版出 `**Hello**, World`——只有 **`Hello`** 被加粗排版。在这里，单个标记 $$\text{abc}*\text{token}$$ 被作为宏标记的参数处理 $$\text{foo}*\text{token}$$，使标记 $$\text{xyz}\_\text{token}$$ 保持不变，其内容按当前字体排版。

## \expandafter 与带参数宏的说明

使用时 `\expandafter` 为了强制展开宏，值得了解宏展开是如何工作的——尤其是对于接受参数的宏。在 TeX 能运行一个宏——即读取并处理宏定义中包含的标记——之前，TeX 需要通过执行初始的宏展开过程，使该宏“准备运行”。如果一个宏的定义包含参数的使用（`#1`, `#2`, ... `#9`），宏展开过程的一部分要求 TeX 扫描输入，寻找由用户提供的参数所组成的标记：这些参数标记会被吸收（从输入中移除）。在宏展开期间，TeX 会从输入中读取并吸收标记，创建小型标记列表，每个参数对应一个列表；随后在 TeX 执行宏时，这些标记列表会被插入到宏主体中的适当位置。宏展开的最后一步包括 TeX 定位存储在内存中的宏定义，并将该位置设为 TeX 读取下一组输入标记的来源。宏的执行在 TeX 开始读取并处理这些标记时启动，同时输入先前创建用于存放参数的标记列表。

### 分隔符标记也会被吸收

如果原始宏定义也使用了充当分隔符的标记，TeX 还需要将原始宏定义与用户对该宏的使用（调用）进行比较，寻找并匹配分隔符标记。一旦匹配/定位，这些分隔符标记随后就会被忽略，因为它们唯一的作用是充当“标点”，帮助 TeX 挑出并识别将要构成每个参数的实际标记。

&#x20;[第 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/18-how-does-expandafter-work-a-detailed-study-of-consecutive-expandafter-commands.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.
