> 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/vi/chu-de-khac/24-how-tex-macros-actually-work-part-6.md).

# Các macro TeX thực sự hoạt động như thế nào: Phần 6

[Phần 1](/latex/vi/chu-de-khac/19-how-tex-macros-actually-work-part-1.md) [Phần 2](/latex/vi/chu-de-khac/20-how-tex-macros-actually-work-part-2.md) [Phần 3](/latex/vi/chu-de-khac/21-how-tex-macros-actually-work-part-3.md) [Phần 4](/latex/vi/chu-de-khac/22-how-tex-macros-actually-work-part-4.md) [Phần 5](/latex/vi/chu-de-khac/23-how-tex-macros-actually-work-part-5.md) [Phần 6](/latex/vi/chu-de-khac/24-how-tex-macros-actually-work-part-6.md)

## Giới thiệu và tổng quan: Câu chuyện cho đến nay

Trong 5 phần trước của loạt bài này, chúng ta đã thấy:

* cách TeX đọc các ký tự trong một tệp đầu vào và sử dụng mã danh mục để nhận diện các “lớp” ký tự khác nhau rồi chuyển chúng thành token ký tự và token lệnh;
* rằng một macro, trên thực tế, gồm bốn phần:

```
<nguyên thủy macro TeX><tên macro><văn bản tham số>{<văn bản thay thế>}
```

trong đó:

* `<nguyên thủy macro TeX>` = một trong `\def`, `\edef`, `\gdef` hoặc `\xdef`;
* `<macro name>`= tên của macro của bạn, chẳng hạn như `\foo`;
* `<parameter text>` có thể là “null” (không có mặt) hoặc có thể là một chuỗi các token phân tách và token tham số macro;
* `<replacement text>` là phần thân thực sự của macro của bạn: phần được “thực thi” (mở rộng) khi bạn gọi macro.
* cách mà `<parameter text>` phần này có thể chứa rất nhiều token và rằng TeX dùng phần này như một “mẫu token” để khớp một lời gọi macro với định nghĩa gốc của nó và suy ra các đối số được dùng với macro—và cách TeX mong đợi việc bạn dùng macro khớp với định nghĩa gốc của nó;
* rằng, bên trong TeX, một định nghĩa macro được lưu dưới dạng một chuỗi token liên tục biểu diễn `<parameter text>` và `<replacement text>` các phần.

Khi bạn dùng một lệnh macro, TeX sẽ trước tiên kiểm tra xem nó có nhận bất kỳ tham số nào không. Nếu có, TeX sau đó phải xác định các *đối số* thực sự được dùng trong lời gọi macro của bạn. TeX phải kiểm tra lời gọi macro của bạn đối chiếu với định nghĩa “mẫu token” mà nó đã lưu trong bộ nhớ. Cụ thể, TeX dùng định nghĩa (được lưu) nội bộ của `<parameter text>` phần *token* để làm mẫu qua đó nó có thể tách ra *token* những token chỉ đóng vai trò là dấu phân tách.

## Ý nghĩa của việc mở rộng macro

Giờ thì cuối cùng chúng ta đã sẵn sàng chuyển sang chủ đề quan trọng nhất: TeX xử lý đối số macro như thế nào và thực sự thực thi macro ra sao: một quá trình mà TeX gọi là *mở rộng macro*.

### Nhưng trước hết, một ví dụ ngắn: có gì đó lạ lùng?

Để “đặt bối cảnh” cho việc giải thích cơ chế mà TeX dùng để xử lý macro và các đối số của chúng, chúng ta sẽ dùng một ví dụ ngắn để chỉ ra những vấn đề cần xem xét.

#### Đối số trước tiên được chuyển thành token

Ví dụ sau dựa trên một ví dụ được thảo luận ở các trang 114–5 của [The Advanced TeXbook](https://www.amazon.co.uk/Advanced-Texbook-David-Salomon/dp/0387945563) do David Salomon viết. Ví dụ này được chọn vì nó gói gọn rất khéo các ý tưởng cốt lõi trong một macro TeX rất ngắn.

Trong quá trình TeX/LaTeX hoạt động bình thường, ký hiệu `$` có mã danh mục 3 (“dịch toán”), mã này chuyển TeX vào/ra chế độ toán nội tuyến (`$...$`) hoặc chế độ toán hiển thị (`$$...$$`)—dĩ nhiên, LaTeX dùng `\(..\)` và `\[..\]` cho cùng mục đích.

Giả sử chúng ta muốn một macro thay đổi mã danh mục của một `$` ký hiệu thành, chẳng hạn, 11 để chúng ta có thể dàn nó như một ký tự thông thường. Chúng ta có thể dùng lệnh nguyên thủy TeX `\catcode` và thử đầu tiên cho một macro như vậy, `\docat`, có thể là

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

Tuy nhiên, khi chúng ta thử dùng nó, như thế này

```
\begin{document}
\def\docat #1{\catcode`\$=11 #1}
Tôi đã trả \docat{$90} cho cuốn sách đó.
\end{document}
```

chúng ta kỳ vọng TeX sẽ dàn `Tôi đã trả $90 cho cuốn sách đó.` nhưng nó thất bại với một thông báo lỗi:

```
! Thiếu dấu $.
<văn bản được chèn>
                $
<to be read again>
                   \par
l.7
```

Từ lỗi này, có vẻ như `$` được dùng trong đối số của macro chúng ta vẫn đang kích hoạt TeX dàn toán; rõ ràng là TeX *đã không* thay đổi mã danh mục của `$` được dùng trong đối số của macro chúng ta (`$90`). Câu hỏi là *vì sao* tại sao TeX lại không thay đổi mã danh mục của `$` thành 11 và dàn nó như một ký tự thông thường? Câu trả lời ngắn là TeX trước tiên chuyển các đối số macro thành token **trước khi** nó nạp chúng vào danh sách token của `<replacement text>`—nhưng chúng ta sẽ xem các cơ chế bên dưới chi tiết hơn nhiều.

Điều chúng ta cần nhớ là quan niệm của chúng ta về việc TeX dùng văn bản/ký tự chỉ liên quan đến nội dung của tệp mà TeX đang đọc: ngay khi TeX đã đọc vào bất kỳ ký tự nào, chúng ta đã bước vào thế giới của *token*. Các lời gọi macro TeX làm việc với *token*, không phải với *biểu diễn bằng văn bản/viết tay* của các lệnh TeX/LaTeX—điều này sẽ rõ hơn khi chúng ta theo dõi ví dụ.

Ban đầu, chúng ta có thể nghĩ rằng việc dùng `\docat` macro trong `Tôi đã trả \docat{$90} cho cuốn sách đó.` giống như trực tiếp viết mã TeX (hoặc LaTeX) tương đương—chẳng hạn như sau đây, điều mà *thì* sẽ hoạt động:

```
\begin{document}
Tôi đã trả \catcode`\$=11 $90 cho cuốn sách đó.
\end{document}
```

![Một số mã TeX đang chạy trên Overleaf](/files/d07bac63cc894733317a15cf85cc79fb2c33cfd1)

Tuy nhiên, như chúng ta đã thấy ở trên, cách TeX xử lý các đối số macro tạo ra một kết quả (`! Thiếu dấu $.`) rất khác với việc viết ra mã TeX: giờ chúng ta sẽ khám phá *vì sao* điều đó xảy ra như thế nào.

### Macro và đối số dưới dạng danh sách token

Để hiểu đầy đủ hành vi của `\docat` macro, và đối số của nó (`$90`), và tại sao nó thất bại, chúng ta lại cần hình dung định nghĩa của `\docat` macro và mọi đối số được dùng (khi \docat được gọi) như là *các danh sách token*, chứ không phải như một chuỗi ký tự.

Khi TeX quét văn bản đầu vào của bạn, nó sẽ nhận ra `\docat` là một lệnh macro; sau đó nó kiểm tra xem nó có nhận bất kỳ tham số nào không—cách TeX làm điều đó được giải thích ở phần sau dành cho những độc giả quan tâm đến các chi tiết nhỏ hơn.

#### Dành cho những ai thích chi tiết...

Sau khi macro được gọi, TeX kiểm tra xem token đầu tiên nhất (trong danh sách token định nghĩa đã lưu của macro) có phải là **dấu khớp kết thúc** token: nếu có, TeX có thể chắc chắn rằng macro không nhận bất kỳ tham số nào.

**Một ví dụ**

Các sơ đồ danh sách nút sau đây so sánh các danh sách token cho hai macro:

* `\def\foo A#1B{#1}`: macro này có `<parameter text>` của `A#1B`, do đó **dấu khớp kết thúc** token là **không** token đầu tiên nên TeX sẽ tiếp tục tìm tham số;
* `\def\foo{X}`: macro này không có một `<parameter text>` phần **dấu khớp kết thúc** , do đó token

![Cách TeX kiểm tra xem một macro có nhận tham số hay không](/files/0b423722971a8a5dfef991aa9d5b6c27c91da4ee)

## Hướng tới “cái kết lớn”: mở rộng

Hãy nhắc lại câu hỏi: tại sao macro sau đây không hoạt động; tức là, tại sao TeX không thay đổi mã danh mục của bất kỳ `$` ký hiệu nào được dùng trong đối số của `\docat` macro, chẳng hạn như `\docat{$90}`?

```
\begin{document}
\def\docat #1{\catcode`\$=11 #1}
Tôi đã trả \docat{$90} cho cuốn sách đó.
\end{document}
```

Như đã giải thích ở trên, khi TeX quét đầu vào của bạn và nhận ra một lệnh macro—vào thời điểm TeX sẽ thực thi nó—TeX trước tiên kiểm tra xem macro đó có nhận bất kỳ tham số nào không. Nếu có, TeX sẽ cần quét thêm tệp đầu vào để xác định các *đối số* mà người dùng đã cung cấp cho lời gọi macro cụ thể này: TeX phải làm việc này **trước khi** nó có thể gọi mã macro thực tế. Rõ ràng, TeX cần xác định dữ liệu mà người dùng muốn cung cấp cho macro.

Để xác định các đối số có trong đầu vào (lời gọi macro của người dùng), TeX sẽ dựa vào định nghĩa của macro đã được lưu bên trong: cụ thể là, `<parameter text>` phần *token* của định nghĩa macro đã lưu (danh sách token)—phần này cung cấp một loại “mẫu token”. Dùng “mẫu token” đó TeX phải xác định những *dấu phân tách* trong lời gọi macro của người dùng chỉ là *token* (về cơ bản là “dấu câu”) và những *lập thành một*đối số **tham số khớp** token trong định nghĩa macro đã lưu `<parameter text>` phần (“mẫu token”) mà nó biết để bắt đầu hình thành một *danh sách token* cho đối số cụ thể đó.

Ngay khi TeX nhận ra cần xác định đối số của người dùng, TeX sẽ quét đầu vào để tạo token và kiểm tra chúng rất cẩn thận, từng token một, đối chiếu với định nghĩa macro đã lưu. TeX tiếp tục thu thập token cho một đối số cho đến khi nó phát hiện một token thực sự là dấu phân tách, hoặc nếu nó phát hiện **dấu khớp kết thúc** token: trong cả hai trường hợp, TeX sẽ biết đã đến lúc dừng tìm các token thuộc phần của đối số đó.

### Tại sao macro \docat thất bại

Như đã lưu ý, *trước khi* Để TeX thực sự gọi một macro, nó phải xác định và chuẩn bị mọi đối số sẽ được dùng với macro đó. Tuy nhiên, để xác định các đối số, sẵn sàng nạp vào macro, TeX phải tạo từng đối số dưới dạng một *danh sách token*: và đó là lý do cho `\docat`sự thất bại của

Trong ví dụ của chúng ta, chúng ta đã cung cấp `\docat` cho nó một đối số là `$90` nhưng đối số đó trước tiên được *chuyển thành một danh sách token* khi TeX quét lời gọi macro—đối số được chuyển thành token *trước khi* trước khi macro thực sự được gọi. Ở đây, đối với đối số `$90`, TeX sẽ tạo ra ba token ký tự: một token cho mỗi `$`, `9` và `0`.

Đồ họa sau đây cho thấy danh sách token được tạo cho đối số `$90`, trước khi được nạp vào thân của `\docat` macro:

![Danh sách token TeX được tạo cho một đối số macro](/files/1ca0bcc087a2726a6c25bdd7da7dc81b6ffb03fb)

Trong đồ họa trên, chúng ta có thể thấy rõ rằng danh sách token của đối số chứa `$` như một token ký tự dựa trên mã danh mục 3.

Như chúng ta đã thấy ở Phần 1 đến 3, token ký tự được tạo bằng các giá trị mã danh mục *đang có hiệu lực vào thời điểm ký tự được đọc vào*—tức là, vào thời điểm danh sách token của đối số được tạo (chuyển thành token). Vào thời điểm các đối số đang được token hóa, macro `\docat` chưa được thực thi nên thay đổi mã danh mục mà chúng ta đặt trong lời gọi macro (``\catcode`\$=11``) *không* không ảnh hưởng đến mã danh mục đang được dùng để tạo token cho đối số.

Một khi TeX đã tạo ra một danh sách token biểu diễn đối số của `$90`, ba token ký tự đó sẽ được nạp vào macro thực tế `<replacement text>`. Tuy nhiên, điều đó khiến `$` được nạp vào như một *token ký tự* được tạo bằng mã danh mục 3: “bật toán” và chúng ta đã thấy rằng một khi một token ký tự được hình thành, mã danh mục đi kèm là vĩnh viễn. Token `$` là *không* nạp vào macro *không phải như một ký tự*, mà như một *token ký tự* dựa trên việc `$` có mã danh mục 3.

### Chạy \docat: mở rộng macro

TeX gọi quá trình “thực thi” một macro là *mở rộng macro*; theo tác giả này, đây là một thuật ngữ hơi gây nhầm lẫn nhưng đó là thuật ngữ được chấp nhận nên chúng ta sẽ tiếp tục dùng nó.

#### Ý nghĩa thực sự của việc mở rộng macro

Sau khi TeX phát hiện `\docat` lệnh trong đầu vào của người dùng, nó quét các đối số và tạo một danh sách token cho đối số của nó (`$90`). Để thực thi (mở rộng) macro, TeX chuyển ánh nhìn khỏi tệp đầu vào của người dùng và bắt đầu đọc các token chứa trong `\docat`của `<replacement text>` danh sách token được lưu trong bộ nhớ của TeX.

Khi TeX xử lý `\docat`định nghĩa của nó, nó sẽ nhìn thấy, rồi thực thi, chuỗi token ban đầu được dùng để định nghĩa macro (`**mã danh mục**`, ``**`**``, `**\$**`, `**=**`, `**1**`, `**1**`, `**#1**`).

Đồ họa sau đây cho thấy quá trình mở rộng `\docat` macro: TeX ngừng lấy token từ tệp đầu vào và bắt đầu đọc token từ `<replacement text>` phần `\docat` token tham số đầu ra **token hướng dẫn TeX đọc (“chèn”) và “thực thi” các token của đối số tại điểm này. Trong ví dụ của chúng ta, đó là ba token ký tự biểu diễn** và điều đó dẫn đến lỗi vì token ký tự đã được chuẩn bị sẵn cho `$90` có mã danh mục 3. Vì chúng ta đang xử lý các token ký tự, không phải các ký tự, nên `$` không bị ảnh hưởng bởi thay đổi mã danh mục trước đó do các token trong `$` Hiển thị quá trình mở rộng macro \docat ``\catcode`\$=11``.

![Sau khi TeX đã xử lý các token biểu diễn](/files/57f63447ccf41f5ba78bdc923eafcf5eee0b421f)

, thay đổi mã danh mục cho ``\catcode`\$=11``giờ sẽ có hiệu lực. TeX sau đó gặp “token đặc biệt” gọi là `$` điều này bảo TeX **token hướng dẫn TeX đọc (“chèn”) và “thực thi” các token của đối số tại điểm này. Trong ví dụ của chúng ta, đó là ba token ký tự biểu diễn** danh sách token cho đối số. Tuy nhiên, danh sách token đó gồm ba token ký **chèn** danh sách token cho đối số. Tuy nhiên, danh sách token đó gồm ba token ký *token*, token đầu tiên trong số đó là một token cho một `$` với mã danh mục 3 (“bật toán”) gán cho nó: thay đổi mã danh mục trước đó bên trong macro không thể ảnh hưởng đến token ký tự này nên TeX coi token đó là tín hiệu để bắt đầu xử lý toán, điều này khiến macro thất bại.

### Có thể sửa macro \docat không?

Từ những thảo luận ở trên, rõ ràng là mọi ký tự xuất hiện trong đối số macro đều được token hóa bằng các mã danh mục đang có hiệu lực tại thời điểm việc token hóa diễn ra—trong ví dụ của chúng ta, đó là *luôn luôn* trước khi `<replacement text>` của `\docat` macro thực sự được thực thi. Vậy, làm sao chúng ta có thể đảm bảo rằng các đối số của macro được thay đổi mã danh mục?

Một cách là sửa `\docat` thành một macro không có tham số, chỉ thực hiện thay đổi mã danh mục—nó không có bất kỳ đối số nào để token hóa. Sau đó chúng ta dùng một macro thứ hai, `\getarg`, macro này nhận một tham số duy nhất, và sắp xếp để đối số của macro đó được token hóa khi mã danh mục thích hợp cho `$` đang có hiệu lực.

```
\begin{document}
\def\docat{\catcode`\$=11 \getarg} % Không có tham số, gọi một macro thứ hai \getarg
\def\getarg#1{#1} % 1 tham số mà đối số của nó sẽ được token hóa
Bây giờ bạn có thể chạy nó như thế này và nó sẽ hoạt động:

Tôi đã trả \docat{$90} cho cuốn sách đó.
\end{document}
```

Khi chúng ta dùng phiên bản mới của `\docat` (như thế này `\docat{$90}`) thì nó *có vẻ* như thể `$90` vẫn đang được dùng làm đối số cho `\docat` macro. Tuy nhiên, như đã thảo luận ở trên, khi TeX phát hiện `\docat` trong đầu vào nó kiểm tra xem nó có bất kỳ đối số nào không: giờ thì không có, nên TeX tiến hành thực thi (mở rộng) nó. Phần mở rộng của `\docat` là chuỗi token `**mã danh mục**`, ``**`**``, `**\$**`, `**=**`, `**1**`, `**1**`, `**khoảng trắng**`, `**getarg**` và điều này diễn ra *trước khi* TeX bắt đầu đọc (token hóa) các ký tự tiếp theo có trong tệp đầu vào—tức là, nhóm `{$90}`. Hãy nhớ rằng khi TeX mở rộng một macro, nó nhận đầu vào tiếp theo bằng cách đọc các token chứa trong danh sách token của định nghĩa macro đó; tức là từ `<replacement text>` phần

TeX sẽ xử lý, rồi thực thi, phần mở rộng của `\docat` và phát hiện *token* **`getarg`**, nhận ra nó là một token biểu diễn một lệnh có tham số. Lúc này, TeX sẽ quét tệp đầu vào để tìm **`getarg`**&#x111;ối số của `{$90}`: các ký tự: `\docat`. Thông thường, các ký tự này được token hóa nhưng vì TeX đã đọc và xử lý phần mở rộng của `$90` , các ký tự `$` được token hóa khi mã danh mục của`<replacement text>`) của `\getarg` chỉ đơn giản là `#1` có nghĩa là dàn đối số được cung cấp, và đó là điều xảy ra, tạo ra một `$` với mã danh mục 11 được tạo ra và dàn an toàn.

## Nhận xét kết luận: câu chuyện trong các nút

Chuỗi sự kiện phát sinh từ việc viết lại `\docat` để dùng macro `\getarg` được chứa trong sơ đồ danh sách nút có chú giải sau đây, sơ đồ này cho thấy quá trình mở rộng của macro `\docat`. Những độc giả muốn nghiên cứu kỹ sơ đồ này có thể tải đồ họa về dưới dạng [tệp PDF](https://assets.ctfassets.net/nrgyaltdicpt/7MOBdavza4WxAEQGISEEht/cabe5097d9063a6415bc553cd38237e6/newdocatexpansion.pdf) hoặc [tệp SVG](https://images.ctfassets.net/nrgyaltdicpt/2FIqqVRXjakdAzTpZsV0m9/10ccc1353741d42d3df18f1692d8aa84/newdocatexpansion--plain.svg) để dùng ngoại tuyến.

![Hiển thị quá trình mở rộng macro \docat đã sửa đổi và macro \getarg](/files/ac3653462f4b84385a5670f73af299a8ceb8e3d2)

[Phần 1](/latex/vi/chu-de-khac/19-how-tex-macros-actually-work-part-1.md) [Phần 2](/latex/vi/chu-de-khac/20-how-tex-macros-actually-work-part-2.md) [Phần 3](/latex/vi/chu-de-khac/21-how-tex-macros-actually-work-part-3.md) [Phần 4](/latex/vi/chu-de-khac/22-how-tex-macros-actually-work-part-4.md) [Phần 5](/latex/vi/chu-de-khac/23-how-tex-macros-actually-work-part-5.md) [Phần 6](/latex/vi/chu-de-khac/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/vi/chu-de-khac/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.
