> 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-tw/shen-ru-wen-zhang/20-how-does-expandafter-work-from-basic-principles-to-exploring-tex-s-source-code.md).

# \expandafter 如何運作：從基本原理到探索 TeX 原始碼

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

## 簡介

現在我們已涵蓋完整探討以下內容所需的背景主題： `\expandafter`:

* TeX token 的基本概念及其計算方式；
* TeX 展開程序背後的原理；
* TeX 在文件處理期間對暫存 token 串列的使用／建立；
* TeX 如何使用及「調度」多個輸入來源（包括暫存 token 串列）。

本文將把這些主題／概念結合起來，說明 TeX 的 `\expandafter` 命令背後的機制：簡而言之，就是它如何運作。

## 那麼，來談談 \expandafter

背後的概念 `\expandafter` 是強制在 TeX 通常會進行展開之前，先展開一個命令（token）。給定兩個 token， $$\mathrm{T\_1}$$ 和 $$\mathrm{T\_2}$$ 的動作 `\expandafter` $$\mathrm{T\_1T\_2}$$ 會使 TeX 處理 $$\mathrm{T\_1}\text{<}$$對以下內容的展開： $$\mathrm{T\_2}\text{>}$$，其中 $$\text{<}\dots\text{>}$$ 表示一個 token 串列。TeX 會展開 $$\mathrm{T\_2}$$ 以便讓 token $$\mathrm{T\_1}$$ （例如原始指令或巨集）能夠看到或作用於展開以下內容所產生的 token： $$\mathrm{T\_2}$$。若 token $$\mathrm{T\_2}$$ 代表不可展開的項目，例如非作用中字元或（大多數）原始指令，則 `\expandafter` 不會改變任何事：TeX 會繼續處理 token $$\mathrm{T\_1T\_2}$$ ，方式與平常相同。

### \expandafter 的使用簡介

如果您尚未使用過 `\expandafter`，以下是將它與原始指令搭配使用的範例 `\uppercase{...}`。假設我們想要排版主 `.tex` 輸入檔的名稱，但要以大寫字母顯示。我們可能知道以下 TeX 原始指令：

* `\uppercase`：顧名思義，將字元 token 轉換為其對應的大寫字元（若存在）；
* `\jobname`：如同我們所見，會展開為主 `.tex` 檔案中使用的指令。

假設我們的 TeX 檔案名為 `mycode.tex` 我們可能合理地預期 `\uppercase{\jobname}` 會排版為 `MYCODE`。但不是，它排版為 `mycode` ，仍是小寫。哪裡「出錯」了？

若我們將以下項目的一般用法 `\uppercase` 寫成

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

，我們可以說 `\uppercase` 會檢視 `<token list>` ，且只會對其中偵測到的 *字元 token* 進行操作（變更大小寫），也就是在 `<token list>`中的項目：所有非字元 token 都會被 *忽略* ，因為 `\uppercase` 不會「查看內部」（展開）非字元 token，以了解它們包含或代表什麼。由於 token 只是一個整數值， `\uppercase` 所需做的就是檢視 token 串列，檢查其中每個 token 的數值是否 `<token list>` 落在表示字元 token 的數值範圍內。順帶一提， `\uppercase` 也會 *變更作用中字元的大小寫* ，以建立大寫的作用中字元；由於它仍是作用中的，因此也必須已被定義，否則 TeX 會產生錯誤： `未定義的控制序列`，不過我們離題了……

例如，即使我們定義一個僅包含文字的巨集

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

，那麼 `\uppercase{\foo}` 仍會排版為 `some lower-case text` ，而不是 `SOME LOWER-CASE TEXT` ，這與我們的期望不同，僅僅是因為 `\uppercase` 不會嘗試判斷 `\foo` 代表什麼：它將 `\foo` 視為命令 token 並忽略它，就如同處理 `\jobname`.

### 要如何修正？\expandafter 來救援

若要排版檔名的大寫版本，我們必須修改 `\uppercase{\jobname}` ，方法是在 `\jobname` 開始工作之前，強制 TeX 將其替換為展開結果（一串字元 token） *，在* `\uppercase` 開始工作之前。再次地，展開被用來移除 `\jobname` token（命令），並將其替換為展開結果（包含字元 token 的 token 串列）。因此，若我們寫下

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

，便能成功： `MYCODE` 會被排版。所發生的是，TeX 開始處理 `\uppercase` 並立即檢查必要的左大括號字元（`{`）；然而，TeX 偵測到一個 `\expandafter` 命令，這使它暫時「轉移注意力」去處理 `\expandafter{\jobname}`.

若我們比較

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

與我們的範例

`\expandafter{\jobname}`

，便可看出

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

其中 `{`<sub>token</sub> 和 `\jobname`<sub>token</sub> 指的是 TeX 所計算的 token 值——下標記法 <sub>token</sub> 用來提醒我們，TeX 在整數 token 的世界中運作。

寫下 `\uppercase\expandafter{\jobname}` 之所以可行，是因為概括來說（細節稍後說明）， `\expandafter` 使 TeX 執行下列工作：

1. 讀取並儲存起始的 `{`<sub>token</sub>;
2. 讀取下一個 token： `\jobname`<sub>token</sub>。TeX 辨識出 `\jobname`<sub>token</sub> 代表可展開的命令，並將其展開。 `\jobname`<sub>token</sub> 會被其展開結果——一連串字元 token——取代；
3. 在展開 `\jobname` 命令後，TeX 將 `{`$$\_\mathrm{token}$$ 「放回輸入中」，並使用由以下項目展開產生的 token 串列 `\jobname` ，使得 TeX 會讀取 `\uppercase{`<sub>token</sub>`<\jobname 的展開結果>`<sub>token 串列（字元）</sub>`}`，這便產生了我們想要的結果。

下圖顯示 TeX 如何處理 `\uppercase\expandafter{\jobname}`——請由下往上閱讀圖示，以跟隨處理流程。

![\expandafter 的運作方式](/files/7254b7e926fe3b53eee7b31b4f6db5f8b7e875d7)

以下說明闡釋處理的各個階段。

1. TeX 開始處理 `\uppercase` ，並檢查必要的左大括號字元（`{`），但偵測到一個 `\expandafter` 指令。
2. 若我們比較 `\expandafter` $$\mathrm{T\_1T\_2}$$ 加入我們的輸入 `\expandafter{\jobname}` ，便可看出 $$\mathrm{T\_1} =$$`{`<sub>token</sub> 和 $$\mathrm{T\_2} =$$`\jobname`<sub>token</sub>。請注意，此處我們將使用下標 <sub>token</sub> 表示 TeX 正在處理整數 token 值。
3. `\expandafter` 讀取，然後暫時儲存， `{`<sub>token</sub> ，方法是將該整數 token 值存入內部變數。稍後，TeX 會在處理完以下項目後，將該 token 重新插回輸入中 `\jobname` 指令。
4. `\expandafter` 讀取下一個 token， `\jobname`<sub>token</sub>，並展開 `\jobname` 指令。
5. 以下項目的展開： `\jobname` 會建立暫存 token 串列，其中包含一連串代表 `.tex` 檔名的字元 token。請注意，由 `\jobname` 產生的所有字元 token 都使用類別碼 12 計算。
6. 一旦 `\jobname` 已展開，TeX 便重新插入步驟 3 中儲存的 token（`{`<sub>token</sub>），並將其放回輸入中。TeX 藉由建立另一個包含以下內容的 token 串列來完成此事 *單一* `{`<sub>token</sub>
7. TeX 現已完成處理 `\expandafter`，因此產生兩個準備作為 TeX 輸入來源的 token 串列。TeX 現在恢復處理 `\uppercase` ，但已設定其輸入，使得由以下項目建立的兩個 token 串列 `\expandafter` 成為以下項目的 token 來源 `\uppercase`——現在它看到的是 `\uppercase{`<sub>token</sub>`<\jobname 的展開結果>`<sub>token 串列（字元）</sub>`}`. `\uppercase` 現在看到一連串字元 token，並能產生我們想要的結果。
8. 讀取由以下項目產生的所有字元 token 後 `\jobname`，TeX 恢復從先前的輸入來源（我們的 `.tex` 檔案）取得 token，並從那裡讀取下一個 token：結尾的 `}` ，這是終止由以下項目處理之 token 串列所必需的 `\uppercase`.

### \expandafter 與內部 token 串列

暫存 token 串列是 *至關重要的* 元素，屬於 `\expandafter`的處理行為：理解那些 token 串列的使用與存在，有助於釐清 `\expandafter` 如何達成其結果，尤其是在嘗試撰寫或理解使用多個連續 `\expandafter` 命令來達成較複雜 token 處理形式的巨集時： `\expandafter\expandafter\expandafter...`

另一個關鍵要素是 `\expandafter`的行為，特別是在有多個連續 `\expandafter` 命令時，便是使用 *遞迴* （在 TeX 軟體本身內）——本文稍後將探討此主題。

為了進一步協助理解暫存 token 串列，我們將再看一個 `\expandafter`的範例，這次搭配 `\the` 指令。

#### \expandafter 與內部 token 串列：範例 2

在此範例中，我們將看到 `\expandafter` 如何用來影響透過以下項目儲存在 token 暫存器中的 token `\toks` 命令。以下是我們將使用的 TeX 原始指令：

* `**\count** *暫存器*=*數值*`：用來儲存數值 `*數值*` 至 TeX 位置 `*暫存器*`;
* `**\toks** *暫存器*={*token 串列*}`：用來儲存 `*token 串列*` 至 token 暫存器位置 `*暫存器*`——儲存一連串 token 供日後使用；
* `**\the** *token*`：一個可展開的 TeX 原始指令，會處理 `*token*`，但確切結果取決於所處理 `*token*` 的性質。 `\the` 有多種用途：其中之一是排版儲存在 TeX 參數或變數中的值（例如暫存器）。其他 `\the` 用途包括插入儲存在 token 暫存器中的 token 複本。此處我們將使用 `\the` 來排版儲存在 `\count` 暫存器中的值。

我們將從下列 TeX 程式碼開始，以儲存數值 `12345` 至 TeX 的 `\count` 暫存器 `99`:

```
\count99=12345
```

若我們想排版儲存在 `\count99` 中的值，可以使用 `\the\count99` （或 `\number\count99`).

接下來，我們將使用 `\toks` 命令，將一些 token 儲存在 token 暫存器 `99`:

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

儲存在 token 暫存器 `99` 中的 token 串列會包含以下項目：

|                 |                                                                 |
| --------------- | --------------------------------------------------------------- |
| **TeX token 值** | **所代表的項目**                                                      |
| 5382            | `\the`                                                          |
| 7885            | `\count`                                                        |
| 3129            | `9` （字元碼 57、類別碼 12），產生 token 值 $$256 \times 12 + 57 = 3129$$    |
| 3129            | `9` （字元碼 57、類別碼 12），產生 token 值 $$256 \times 12 + 57 = 3129$$    |
| 2592            | `<空格>` （字元碼 32、類別碼 10），產生 token 值 $$256 \times 10 + 32 = 2592$$ |

請注意，由以下項目建立的 token 串列 `\toks99` 確實 *不* 包含實際儲存在以下項目中的資料值 `\count99` ，因為 `\toks` 命令不會進行展開：它只是建立並儲存 token。在我們的範例中， `\the` 不會被展開，因此不會處理 `\count99`；此處 `\the` 僅被轉換為一個 token（值為 5382），並儲存在 token 串列中。

若我們希望 `\toks99` token 串列包含代表儲存在以下項目中資料的 token `\count99` ，我們需要某種方法來建立那些 token（使其可用），以便 `\toks` 命令能夠存取它們。而當然， `\expandafter` 可以為我們完成此事。若我們寫下：

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

以下項目的動作／處理 `\toks` 命令將會「暫停」，同時 `\expandafter` 導致（強制）展開 `\the` ，接著它會作用於 `\count` 以產生暫存 token 串列，其中包含代表儲存在以下項目中資料的字元 token `\count99`。一個小但重要的要點是， `<空格>` 數字後方的字元 `99`：該 `<空格>` 字元在 TeX 搜尋數值量時，會終止其掃描程序。

此處，以下項目的動作 `\expandafter` 與 `\jobname` 範例非常相似。

1. 讀取並儲存起始的 `{`<sub>token</sub>.
2. 讀取下一個 token， `\the`<sub>token</sub>，其代表可展開命令，因此 TeX 將其展開。 `\expandafter` 強制展開 `\the` ，接著它會作用於 `\count99` 以將儲存在以下項目中的資料 `\count` 暫存器 `99` （數字 12345）轉換為暫存 token 串列。該串列將包含代表數字的字元 token `1`, `2`, `3`, `4` 和 `5`——類別碼為 12 的字元 token。
3. 展開並處理 `\the`後，TeX 將 `{`<sub>token</sub> 「放回輸入中」，並使用由以下項目產生的 token 串列 `\the\count99` ，使得 TeX 會讀取 `\toks99={`<sub>token</sub>`<\the\count99 的展開結果>`<sub>token 串列（字元）</sub>`}` ，這便產生了我們想要的結果。

下圖概述此事件序列——請由下往上閱讀圖示，以跟隨處理流程。

![\expandafter 的運作方式](/files/ba7601d31f0c34483c2487b11bc7eef623bec3dd)

1. TeX 開始處理 `\toks`；它看到可選的 `=` 符號，接著檢查必要的左大括號字元（`{`，或任何類別碼為 1、用來表示 token 串列起始的字元）。不過，TeX 偵測到一個 `\expandafter` 命令，並改為執行它。
2. 若我們比較 `\expandafter` $$\mathrm{T\_1T\_2}$$ 加入我們的輸入 `\expandafter{\the\count99 }` ，便可看出 $$\mathrm{T\_1} =$$ `{`<sub>token</sub> 和 $$\mathrm{T\_2} =$$ `\the<sub>token</sub>`.
3. `\expandafter` 讀取，然後暫時儲存， `{`<sub>token</sub> （TeX 暫時將該整數 token 值儲存在內部變數中）。稍後，TeX 會在處理完以下項目後，將該 token 重新插回輸入中 `\the`
4. `\expandafter` 讀取下一個 token， `\the`<sub>token</sub> 並將其展開。
5. 以下項目的展開： `\the` 從處理以下項目建立暫存 token 串列 `\count99`——該 token 串列包含一連串字元 token，代表儲存在以下項目中的資料值 `\count` 暫存器 `99`.
6. 一旦 `\the` 已展開，TeX 便重新插入步驟 3 中儲存的 token（`{`<sub>token</sub>）並將該 token 放回輸入中。TeX 藉由建立另一個包含以下內容的 token 串列來完成此事 *單一* token `{`<sub>token</sub>.
7. TeX 現已完成處理 `\expandafter` ，並產生兩個準備作為下一個輸入來源的 token 串列。TeX 恢復處理 `\toks99=` ，但現在 TeX 已設定其輸入，使得由以下項目建立的兩個 token 串列 `\expandafter` 成為以下項目的 token 來源 `\toks`——現在它看到的是 `{`<sub>token</sub>`<\the\count99 的展開結果>`<sub>token 串列（字元）</sub>`}`. `\toks` 現在可以存取並儲存代表資料值的 5 個字元 token 序列（`12345`）並儲存在 `\count99`中：這就是我們想要的結果。
8. 讀取由以下項目產生的所有字元 token 後 `\the\count99`，TeX 恢復從先前的輸入來源（我們的 `.tex` 檔案）取得 token，並從那裡讀取下一個 token：結尾的 `}` ，這是終止由以下項目儲存之 token 串列所必需的 `\toks99={...}`.

## \expandafter 真正的運作方式

在本節中，我們將從「低階」角度檢視 TeX 內部：探索實作以下項目行為的 TeX 原始碼／函式 `\expandafter`。細節以類 C 偽程式碼表示，但任何熟悉其他程式語言的人都應能理解。

下列附註圖解說明 TeX 如何實作 `\expandafter` ，作為名為 `expand()`的較大函式一部分——此函式是驅動 TeX 展開處理的核心函式。在負責實作 `\expandafter` ，便可看出 [*遞迴* 行為](https://en.wikipedia.org/wiki/Recursion) 的區段中，會再次呼叫 `expand()` 函式，以處理讀入的第二個 token， $$\mathrm{T\_2}$$，適用於 $$\mathrm{T\_2}$$ 可展開的情況。

雖然此程式碼出現在 Knuth 的 TeX 引擎中，但此圖所概述的基本原理適用於所有 TeX 引擎。

![\expandafter 在 TeX 內部的運作方式](/files/65fe714d6d7a1fa5c60f63a2b88ce0cfeb523543)

以下項目的第一項工作 `expand()` 是判定要展開的命令是巨集還是原始指令，因為巨集有專門的展開程序，由名為以下項目的函式處理 `macrocall()`.

若要展開的命令是原始指令， `expand()` 函式會使用目前的命令碼值（儲存在全域變數 `curcmd`）來識別需要處理的特定原始指令。我們可以在以下項目較完整的清單中看到這些細節 `expand()`:

```
    void expand(void)
    {
    //curcmd 是全域變數
    if(curcmd != macro) // curcmd < 111
    {
      switch(curcmd)
      {
        case \expandafter: // 處理 \expandafter T1T2 指令
        {
            gettoken(); // 讀取 token T1
            t = curtok; // 將 token T1 儲存在區域變數 t 中
            gettoken(); // 讀取 token T2
            if(curcmd > 100) // token T2 是否可展開？
                expand();    // 是！T2 可展開：
                             // 透過
                             // 呼叫遞迴函式 expand() 來展開 T2
            else
                backinput(); // T2 不可展開：將該 token
                             // 放回輸入中，稍後再讀取

            curtok = t ;  // 將全域變數 curtok 還原為已儲存的 T1 值
            backinput() ; // 將 token T1 放回輸入中
                          // 置於由 T2 展開而來的 tokens 之前
        }
        break;

        // 處理其他可展開指令的程式碼
        case “轉為文字” 指令: // \number、\string、\romannumeral、中的任何一個
                                        // \meaning、\fontname、\jobname
                                        // 它們共用相同的 curcmd 值
        break;

        case \noexpand: // 抑制下一個 token 的展開
        ...
        break;

        case \csname:  // 建立控制序列名稱。
        ...
        break;

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

        case “\if... 測試指令” : // 處理 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 引擎支援的任何其他可展開原始指令

        }

    }else // 不是可展開的原始指令：它是一個巨集
        {
             macrocall()
        }
        //... 更多程式碼已移除
    }
```

### TeX 對全域變數的偏愛

或許反映了它的年代與設計時代，TeX 的原始碼大量使用所謂的 [全域變數](https://en.wikipedia.org/wiki/Global_variable)——事實上數量有數百個。全域變數的本質就是可以在 TeX 原始碼中的任何地方被變更／修改——而 Knuth 的 TeX 則是一個單一龐大的檔案，包含超過 25,000 行程式碼與數百個函式。要理解 TeX 的運作方式並不總是件容易的事……

為了處理 `\expandafter`，TeX 會使用一個名為 `gettoken()` 的函式從目前輸入中讀取 tokens，其作用是建立一個 token，並設定 TeX 原始碼中各處會用到的幾個關鍵全域變數的值。其中有兩個變數會隨著 `gettoken()`的作用而更新，並用於 `\expandafter`:

* `curtok`： （目前 token）剛讀入之 token 的整數值；
* `curcmd`： （目前指令碼）由該 token 代表之指令（或字元）的指令碼 `curtok`.

在處理 `\expandafter`$$\mathrm{T\_1T\_2}$$ 時，TeX 會讀取 token $$\mathrm{T\_1}$$ ，並暫時將其值（整數）儲存在名為 `的區域變數中`。接著 TeX 會讀取 $$\mathrm{T\_2}$$ ，並檢查該 token 是否代表可展開指令——方法是檢查其指令碼（`curcmd`）是否大於 100。若是，TeX 需要展開由 $$\mathrm{T\_2}$$ ，並且再呼叫一次函式 `expand()`：這是 *遞迴* ，因為 `expand()` 函式呼叫自身的範例。理解展開的遞迴本質，尤其是在使用 `\expandafter`時，有助於理解多個連續的 `\expandafter` 指令——也就是 `\expandafter\expandafter\expandafter...` ——如何達成其效果。

如果 token $$\mathrm{T\_2}$$ 可展開，展開就會發生，而當對 `expand()` 的遞迴呼叫返回時， `\expandafter` 實作中的程式碼會將 token $$\mathrm{T\_1}$$ 重新插回輸入中。全域變數 `curtok` 會被重新指定為已儲存 token 的值——此值儲存在區域變數 `的區域變數中`中，也就是 token $$\mathrm{T\_1}$$的值——然後會呼叫函式 `backinput()。`

#### 函式 backinput()

顧名思義，這個函式會把 token「放回輸入中」。為了做到這點，TeX 會使用全域變數的目前值 `curtok` 來建立一個 token list，其中包含單一 token（其整數值由 `curtok`提供）。TeX 也會安排其輸入處理，以確保那個單一 token 的 list 會在適當的時候，作為 TeX 後續輸入處理的一部分被重新讀取。請仔細注意，token $$\mathrm{T\_1}$$ 會被重新插入 *在展開完成之後*，這可確保 TeX 會在 *，在* 讀取由 $$\mathrm{T\_2}$$.

### 處理巨集：macrocall() 函式

如前所述，所有巨集，以及某些原始指令，都是可展開的，而所有展開處理都會經由 `expand()` 函式。然而， `expand()` 會小心使用 `curcmd` （目前指令）值來區分可展開原始指令與巨集，因為巨集展開流程是由一個專門的函式 `macrocall()`來處理。巨集需要特殊化的展開流程，因為巨集參數與分隔符 token 必須以非常特定且嚴謹的方式被掃描；因此，這個流程被委派給一個專為此而設的函式： `macrocall()`.

#### 巨集展開 vs. 巨集執行

巨集 *展開* 並不等同於巨集 *執行*：巨集的展開是 TeX 在讓巨集 *準備好執行*之前所進行的前置流程。當 TeX 正在主動讀取並處理該巨集定義（替換文字）及其參數（引數）中所含的 token 時，就發生了巨集的「執行」。

#### 巨集展開

要展開一個巨集，TeX 會先檢查該巨集是否帶有引數；如果有， `macrocall()` 它會非常仔細地掃描輸入，尋找要成為巨集引數的 tokens。這個過程包含檢查使用者輸入中是否有在該巨集原始定義中所用到的任何分隔符 token——在巨集呼叫中所使用的 token 模式，必須與儲存定義中所包含的 token 模式完全一致。然而，作為分隔符使用的 tokens 會被 TeX 直接丟棄：事實上，它們只是 TeX 用來判定哪些 token 真正要成為巨集引數的一種「標點符號」——也就是使用者打算交由巨集處理的 tokens。關於分隔符 token 的更多資訊，請參閱 [TeX 巨集實際上如何運作](/latex/zh-tw/geng-duo-zhu-ti/22-how-tex-macros-actually-work-part-4.md).

對於巨集原始定義中出現的每個參數（`#1, #2...#9`），TeX 會掃描實際的巨集呼叫，以辨識使用者提供的哪些 tokens 是要分配給各個參數的（也就是構成巨集引數）。這個過程會產生一個或多個迷你 token list：每個巨集引數各一個。

在偵測出任何巨集引數，並準備好其 token list 之後，TeX 會從記憶體中取出儲存的巨集定義（替換文字），並安排其輸入處理，使得每當 TeX 準備好要讀取／處理更多 tokens 時，它都會從巨集的替換文字中讀取，藉此執行該巨集。在適當的時機、於巨集執行期間，代表巨集引數的 token list 會被送入巨集替換文字中的正確位置。

再一次，巨集指令的展開是指 *將* 該巨集指令（token）從輸入中移除，並 *以* 儲存為該巨集替換文字的 token list 取代它。

若要深入了解 TeX 的巨集處理，請參閱六篇系列文章 [TeX 巨集究竟是如何運作的？](/latex/zh-tw/geng-duo-zhu-ti/01-a-six-part-series-how-do-tex-macros-actually-work.md)

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