> 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/geng-duo-zhu-ti/24-how-tex-macros-actually-work-part-6.md).

# TeX 巨集實際上如何運作：第 6 部分

[第 1 部分](/latex/zh-tw/geng-duo-zhu-ti/19-how-tex-macros-actually-work-part-1.md) [第 2 部分](/latex/zh-tw/geng-duo-zhu-ti/20-how-tex-macros-actually-work-part-2.md) [第 3 部分](/latex/zh-tw/geng-duo-zhu-ti/21-how-tex-macros-actually-work-part-3.md) [第 4 部分](/latex/zh-tw/geng-duo-zhu-ti/22-how-tex-macros-actually-work-part-4.md) [第 5 部分](/latex/zh-tw/geng-duo-zhu-ti/23-how-tex-macros-actually-work-part-5.md) [第 6 部分](/latex/zh-tw/geng-duo-zhu-ti/24-how-tex-macros-actually-work-part-6.md)

## 前言與概述：目前為止的內容

在本系列前 5 個部分中，我們已經看到：

* TeX 如何讀取輸入檔中的字元，並使用類別碼辨識不同的字元「類別」，接著將其轉換為字元權杖與命令權杖；
* 巨集實際上由四個部分組成：

```
<TeX macro primitive><macro name><parameter text>{<replacement text>}
```

其中：

* `<TeX 巨集原語>` = 下列其中之一 `\def`, `\edef`, `\gdef` 或 `\xdef`;
* `<巨集名稱>`=您的巨集名稱，例如 `\foo`;
* `<參數文字>` 可以是「空」的（不存在），或是一串分隔符權杖與巨集參數權杖；
* `<取代文字>` 是巨集的實際主體：當您呼叫巨集時會被「執行」（展開）的部分。
* 如何讓 `<參數文字>` 部分可包含各式各樣的權杖，而 TeX 使用此部分作為「權杖範本」，以將巨集呼叫與其原始定義比對，並找出與巨集搭配使用的引數——以及 TeX 如何要求您對巨集的使用符合其原始定義；
* 在 TeX 內部，巨集定義會儲存為一段連續的權杖序列，代表 `<參數文字>` 和 `<取代文字>` 部分。

當您使用巨集命令時，TeX 首先會檢查它是否接受任何參數。若是，TeX 接著必須識別實際的 *引數* 用於巨集呼叫中的引數。TeX 必須將您的巨集呼叫與它儲存在記憶體中的「權杖範本」定義進行測試。具體而言，TeX 使用巨集的內部（儲存）定義中的 `<參數文字>` 部分作為範本，藉此擷取出 *記號* 哪些是實際引數，以及哪些 *記號* 僅用作分隔符。

## 巨集展開的意義

現在我們終於準備好進入最重要的主題：TeX 如何處理巨集引數並實際執行巨集——TeX 將此程序稱為 *巨集展開*.

### 但首先，來看一個簡短範例：有些奇怪？

為了「鋪陳背景」來解釋 TeX 處理巨集及其引數的機制，我們將使用一個簡短範例來指出需要考慮的問題。

#### 引數會先轉換為權杖

以下範例根據 [《The Advanced TeXbook》](https://www.amazon.co.uk/Advanced-Texbook-David-Salomon/dp/0387945563) 中第 114–115 頁所討論的範例，該書由 David Salomon 撰寫。選擇它是因為它能在非常簡短的 TeX 巨集中精確地概括核心概念。

在正常的 TeX/LaTeX 操作期間， `$` 符號的類別碼為 3（「數學切換」），它會讓 TeX 進入／離開行內數學模式（`$...$`）或展示數學模式（`$$...$$`）——當然，LaTeX 使用 `\(..\)` 和 `\[..\]` 來達成相同用途。

假設我們想要一個巨集，將 `$` 符號的類別碼改為，例如，11，如此便能像一般字元一樣排版它。我們可以使用 TeX 原語命令 `\catcode` ，而我們第一次嘗試這類巨集， `\docat`，可能是

```
\def\docat #1{\catcode`\$=11 #1}
```

然而，當我們嘗試像這樣使用它時

```
\begin{document}
\def\docat #1{\catcode`\$=11 #1}
I paid \docat{$90} for that book.
\end{document}
```

我們預期 TeX 會排版出 `I paid $90 for that book.` 但它失敗並顯示錯誤訊息：

```
! Missing $ inserted.
<已插入文字>
                $
<再次讀取>
                   \par
l.7
```

從錯誤看來， `$` 在巨集引數中使用的符號仍會觸發 TeX 排版數學內容；顯然，TeX *並未* 變更 `$` 在巨集引數中使用之符號的類別碼（`$90`）。問題是 *為什麼* 為什麼 TeX 沒有變更 `$` 的類別碼為 11，並將其作為一般字元排版？簡短的答案是，TeX 會先將巨集引數轉換為權杖， **，在** 再將它們送入 `<取代文字>`的權杖串列中——但我們會更詳細地探討其底層機制。

我們需要記住的是，TeX 使用文字／字元的概念僅與 TeX 正在讀取的檔案內容有關：一旦 TeX 讀入任何字元，我們便進入 *記號*的世界。TeX 巨集呼叫運作於 *記號*，而非實際的 *書寫／文字表示法* 的 TeX/LaTeX 命令——隨著我們逐步分析範例，這一點會更加清楚。

一開始，我們可能會認為，我們對 `\docat` 巨集在 `I paid \docat{$90} for that book.` 中的使用，等同於直接寫出對應的 TeX（或 LaTeX）程式碼——例如下列程式碼，它 *確實* 可運作：

```
\begin{document}
I paid \catcode`\$=11 $90 for that book.
\end{document}
```

![在 Overleaf 上執行的部分 TeX 程式碼](/files/e7e351db70b1a1fc185c87252542193c4d5aa834)

然而，如同我們先前所見，TeX 處理巨集引數的方式會產生一個結果（`! Missing $ inserted.`），與直接寫出 TeX 程式碼大不相同：現在我們將探討 *為什麼* 這是如何發生的。

### 作為權杖串列的巨集與引數

要完全理解 `\docat` 巨集及其引數（`$90`）的行為與其失敗原因，我們再次需要將 `\docat` 巨集定義及任何使用的引數（呼叫 \docat 時）視覺化為 *權杖串列*，而不是字元序列。

當 TeX 掃描您的輸入文字時，它會將 `\docat` 辨識為巨集命令；之後會檢查它是否接受任何參數——對細節感興趣的讀者可於下一節了解 TeX 如何進行此項檢查。

#### 給喜歡細節的人……

呼叫巨集後，TeX 會檢查巨集儲存的定義權杖串列中的第一個權杖是否為 **結束匹配** 權杖：若是，TeX 就可以確定該巨集不接受任何參數。

**範例**

下列節點串列圖比較兩個巨集的權杖串列：

* `\def\foo A#1B{#1}`：它有 `<參數文字>` 的 token 值 `A#1B`，因此 **結束匹配** 權杖是 **不** 第一個權杖，所以 TeX 會繼續尋找參數；
* `\def\foo{X}`：它沒有 `<參數文字>` 部分，因此 **結束匹配** 權杖是權杖串列中的第一個，TeX 知道不必尋找任何參數。

![TeX 如何檢查巨集是否接受參數](/files/3c31ec07f7773eafa9c691d5e231db1553eb6f22)

## 邁向「最終高潮」：展開

讓我們回顧這個問題：以下巨集為何無法運作；亦即，為何 TeX 不會變更 `$` 在 `\docat` 巨集引數中使用的符號類別碼，例如 `\docat{$90}`?

```
\begin{document}
\def\docat #1{\catcode`\$=11 #1}
I paid \docat{$90} for that book.
\end{document}
```

如上所述，當 TeX 掃描您的輸入並辨識出巨集命令——在它即將執行該命令時——TeX 首先會檢查該巨集是否接受任何參數。若是，TeX 將需要進一步掃描輸入檔，以識別實際的 *引數* 使用者為這次特定巨集呼叫所提供的引數：TeX 必須在 **，在** 呼叫實際巨集程式碼之前完成此事。顯然，TeX 需要判定使用者想提供給巨集的資料。

為識別輸入中出現的引數（使用者的巨集呼叫），TeX 會由該巨集內部儲存的定義引導：具體而言， `<參數文字>` 儲存巨集定義（權杖串列）的部分——它提供了一種「權杖範本」。使用該「權杖範本」，TeX 必須判定使用者巨集呼叫中的哪些 *記號* 只是 *分隔符號* （本質上是「標點符號」），以及哪些 *記號* 構成一個 *引數*。當 TeX 遇到一個 **匹配參數** 儲存巨集定義中的權杖 `<參數文字>` 部分（「權杖範本」）時，它就知道要開始形成一份 *權杖串列* ，供該特定引數使用。

只要 TeX 辨識出有必要識別使用者的引數，它就會掃描輸入以產生權杖，並非常仔細地逐一將其與儲存的巨集定義核對。TeX 會持續收集某個引數的權杖，直到偵測到實際上是分隔符的權杖，或偵測到 **結束匹配** 權杖：無論是哪種情況，TeX 都知道該停止尋找構成該引數的權杖了。

### \docat 巨集為何失敗

如前所述， *，在* TeX 要實際呼叫巨集之前，必須先識別並準備將與該巨集搭配使用的任何引數。然而，為了識別要送入巨集的引數，TeX 必須將每個引數產生為一份 *權杖串列*：而這就是 `\docat`失敗的原因。

在我們的範例中，我們提供了 `\docat` ，其引數為 `$90` ，但該引數首先會 *轉換為權杖串列* ，因為 TeX 掃描巨集呼叫時，會將引數轉換為權杖 *，在* 巨集實際被呼叫之前。此處，對於引數 `$90`，TeX 會產生三個字元權杖：每一個各對應於 `$`, `9` 和 `0`.

下圖顯示為引數產生的權杖串列 `$90`，在送入 `\docat` 巨集：

![巨集引數所產生的 TeX 權杖串列](/files/5f62be89acad4fbe970cee862f6ef64053c25f04)

在上圖中，我們可清楚看到引數權杖串列包含 `$` ，作為根據類別碼 3 所建立的字元權杖。

如同我們在第 1 至第 3 部分所見，字元權杖是使用類別碼值建立的， *這些值在讀入字元時生效*——亦即，建立引數權杖串列（轉換為權杖）時。在將引數權杖化時， `\docat` 巨集尚未執行，因此我們放入巨集呼叫中的類別碼變更（``\catcode`\$=11``) *不會* 不會影響用來產生引數權杖的類別碼。

一旦 TeX 產生代表 `$90`引數的權杖串列，這三個字元權杖便會被送入實際的巨集 `<取代文字>`。然而，這使得 `$` 被送入時是以一個 *字元 token* 使用類別碼 3 建立：「數學開啟」；而我們已經看到，一旦字元權杖形成，附加的類別碼便是永久的。 `$` 是 *不* 被送入巨集的 *不是作為字元*，而是作為一個 *字元 token* ，其依據是 `$` 具有類別碼 3。

### 執行 \docat：巨集展開

TeX 將「執行」巨集的程序稱為 *巨集展開*；筆者認為這個術語有些令人困惑，但它是公認的術語，因此我們將繼續使用它。

#### 巨集展開的真正意義

在 TeX 偵測到 `\docat` 使用者輸入中的命令後，它會掃描引數，並為其引數產生權杖串列（`$90`）。為執行（展開）巨集，TeX 將視線從使用者的輸入檔移開，開始讀取包含在 `\docat`的 `<取代文字>` 儲存在 TeX 記憶體中的權杖串列。

當 TeX 處理 `\docat`的定義時，接著會看到並執行原本用來定義巨集的一系列權杖（`**catcode**`, ``**`**``, `**\$**`, `**=**`, `**1**`, `**1**`, `**#1**`).

下圖顯示展開 `\docat` 巨集的程序：TeX 停止從輸入檔取得權杖，轉而從 `<取代文字>` 部分讀取權杖；該部分位於儲存在記憶體中的 `\docat` 巨集定義中。TeX 持續執行這些預先準備好的權杖，直到看見一個 **輸出參數** 權杖；該權杖指示 TeX 此時讀取（「注入」）並「執行」引數權杖。在我們的範例中，那是三個代表 `$90` 的字元權杖，而這會導致錯誤，因為預先準備的 `$` 字元權杖具有類別碼 3。由於我們處理的是字元權杖而非字元， `$` 不會受到 ``\catcode`\$=11``.

![顯示展開 \docat 巨集的程序](/files/9308335ee725ab5199528133e6374fcc3590f24c)

在 TeX 處理完代表 ``\catcode`\$=11``的權杖後， `$` 的類別碼變更現在將會生效。接著 TeX 會遇到稱為 **輸出參數** 的「特殊權杖」，它告訴 TeX 要 **插入** 引數的權杖串列。然而，該權杖串列包含三個字元 *記號*，其中第一個是代表 `$` 的權杖，並被指定類別碼 3（「數學開啟」）：巨集內先前的類別碼變更無法影響這個字元權杖，因此 TeX 將該權杖視為開始數學處理的訊號，導致巨集失敗。

### 可以修正 \docat 巨集嗎？

根據上述討論可清楚得知，巨集引數中出現的任何字元，都會使用權杖化時生效的類別碼進行權杖化——而在我們的範例中，這 *總是* 在 `<取代文字>` 中的參數 `\docat` 巨集實際執行之前。那麼，我們如何確保巨集引數的類別碼會改變？

一種方式是修改 `\docat` ，使其成為不帶參數且只進行類別碼變更的巨集——它沒有需要權杖化的引數。接著我們使用第二個巨集， `\getarg`，它接受一個參數，並安排該巨集在適當的 `$` 類別碼生效時，將其引數權杖化。

```
\begin{document}
\def\docat{\catcode`\$=11 \getarg} % 無參數，呼叫第二個巨集 \getarg
\def\getarg#1{#1} % 1 個參數，其引數將被權杖化
現在您可以像這樣執行，它將會正常運作：

I paid \docat{$90} for that book.
\end{document}
```

當我們使用新版的 `\docat` （像這樣 `\docat{$90}`）時，它 *看起來* 彷彿 `$90` 仍被用作 `\docat` 巨集的引數。然而，如上所述，當 TeX 在輸入中偵測到 `\docat` 時，它會檢查是否有任何引數：現在它沒有，因此 TeX 會繼續執行（展開）它。 `\docat` 的展開是一串權杖 `**catcode**`, ``**`**``, `**\$**`, `**=**`, `**1**`, `**1**`, `**space**`, `**getarg**` ，而這發生在 *，在* TeX 開始讀取（權杖化）輸入檔中所含的下一批字元之前——亦即，群組 `{$90}`。請記住，當 TeX 展開巨集時，它透過讀取該巨集定義之權杖串列中包含的權杖來取得下一個輸入；亦即，來自記憶體中儲存的 `<取代文字>` 部分。

TeX 將處理並執行 `\docat` 的展開，並偵測到 *權杖* **`getarg`**，辨識它為代表接受參數之命令的權杖。此時，TeX 將掃描輸入檔以尋找 **`getarg`**&#x7684;引數：字元： `{$90}`。如同往常，這些字元會被權杖化；但由於 TeX 已讀取並處理 `\docat`的展開，這些字元 `$90` 會在 `$` 的類別碼已變更為 11 時被權杖化。`<取代文字>`的定義（ `\getarg` ）僅是 `#1` ，意思是排版所提供的引數，而這正是所發生的事，結果產生一個 `$` ，其類別碼為 11，並可安全地排版。

## 結語：以節點呈現的過程

重新撰寫 `\docat` 以使用巨集 `\getarg` 所產生的事件序列，包含在以下附註的節點串列圖中；該圖顯示巨集 `\docat`的展開程序。希望仔細研究此圖的讀者可以將圖形下載為 [PDF](https://assets.ctfassets.net/nrgyaltdicpt/7MOBdavza4WxAEQGISEEht/cabe5097d9063a6415bc553cd38237e6/newdocatexpansion.pdf) 或 [SVG](https://images.ctfassets.net/nrgyaltdicpt/2FIqqVRXjakdAzTpZsV0m9/10ccc1353741d42d3df18f1692d8aa84/newdocatexpansion--plain.svg) 檔案以供離線使用。

![顯示展開修改後的 \docat 巨集與 \getarg 巨集的程序](/files/134e5b90005b4fd6468575008d6dd8207e262325)

[第 1 部分](/latex/zh-tw/geng-duo-zhu-ti/19-how-tex-macros-actually-work-part-1.md) [第 2 部分](/latex/zh-tw/geng-duo-zhu-ti/20-how-tex-macros-actually-work-part-2.md) [第 3 部分](/latex/zh-tw/geng-duo-zhu-ti/21-how-tex-macros-actually-work-part-3.md) [第 4 部分](/latex/zh-tw/geng-duo-zhu-ti/22-how-tex-macros-actually-work-part-4.md) [第 5 部分](/latex/zh-tw/geng-duo-zhu-ti/23-how-tex-macros-actually-work-part-5.md) [第 6 部分](/latex/zh-tw/geng-duo-zhu-ti/24-how-tex-macros-actually-work-part-6.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/geng-duo-zhu-ti/24-how-tex-macros-actually-work-part-6.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.
