> 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/bai-viet-chuyen-sau/17-how-does-expandafter-work-a-detailed-macro-case-study.md).

# \expandafter hoạt động như thế nào: Một nghiên cứu tình huống chi tiết về macro

&#x20;[Phần 1](/latex/vi/bai-viet-chuyen-sau/19-how-does-expandafter-work-an-introduction-to-tex-tokens.md)   [Phần 2](/latex/vi/bai-viet-chuyen-sau/22-how-does-expandafter-work-the-meaning-of-expansion.md)   [Phần 3](/latex/vi/bai-viet-chuyen-sau/21-how-does-expandafter-work-tex-uses-temporary-token-lists.md)   [Phần 4](/latex/vi/bai-viet-chuyen-sau/20-how-does-expandafter-work-from-basic-principles-to-exploring-tex-s-source-code.md)   [Phần 5](/latex/vi/bai-viet-chuyen-sau/17-how-does-expandafter-work-a-detailed-macro-case-study.md)   [Phần 6](/latex/vi/bai-viet-chuyen-sau/18-how-does-expandafter-work-a-detailed-study-of-consecutive-expandafter-commands.md)&#x20;

## Nghiên cứu trường hợp: \expandafter ví dụ từ The $$\varepsilon\mathrm{\text{-}{\TeX}}$$ Sổ tay

Tính năng $$\varepsilon\mathrm{\text{-}{\TeX}}$$ bộ máy dàn trang được phát triển từ phần mềm TeX của Knuth và ban đầu được dự định như một bước “tạm thời” hướng tới việc phát triển [Hệ thống Dàn trang Mới](https://en.wikipedia.org/wiki/New_Typesetting_System) (NTS), được viết bằng ngôn ngữ lập trình Java. $$\varepsilon\mathrm{\text{-}{\TeX}}$$ lần đầu được phát triển vào cuối những năm 1990 nhằm bổ sung một bộ lệnh nguyên thủy mới cung cấp thêm chức năng không có trong chương trình gốc của Knuth. Mặc dù $$\varepsilon\mathrm{\text{-}{\TeX}}$$ đã nhận được các bản cập nhật định kỳ kể từ khi phát hành ban đầu, ngày nay nó không được dùng rộng rãi như một bộ máy dàn trang độc lập, mặc dù các đổi mới của nó đã được hấp thụ vào các thế hệ TeX sau này: pdfTeX, XeTeX và LuaTeX.

Tính năng [$$\varepsilon\mathrm{\text{-}{\TeX}}$$ manual](http://mirror.ox.ac.uk/sites/ctan.org/systems/doc/etex/etex_man.pdf) chứa một ví dụ minh họa sáng tỏ về một macro sử dụng khéo léo `\expandafter`:

```
    \def\foo#1#2{\number#1
    \ifnum#1<#2,
    \expandafter\foo
    \expandafter{\number\numexpr#1+1\expandafter}%
    \expandafter{\number#2\expandafter}%
    \fi}
```

`\foo` triển khai một cơ chế lặp, sao cho `\foo{7}{13}` tạo ra `7, 8, 9, 10, 11, 12, 13`; tuy nhiên, `\foo` không sử dụng bất kỳ *phép gán nào cho biến* để kiểm soát quá trình lặp—điều này khiến nó trở thành một macro thú vị để tìm hiểu chi tiết hơn.

### Một số bối cảnh: biểu thức và phép gán

Một yếu tố quan trọng của `\foo` là việc sử dụng lệnh `\numexpr`, một lệnh trong bộ bốn primitive có liên quan lần đầu được giới thiệu bởi $$\varepsilon\mathrm{\text{-}{\TeX}}$$: `\numexpr`, `\dimexpr`, `\glueexpr` và `\muexpr`. Mục đích của chúng là tạo ra cái gọi là *biểu thức* cho phép tính toán/thao tác các giá trị TeX thuộc kiểu number, dimen, glue hoặc muglue (tương ứng). Như đã thảo luận ở trang 8–9 của The $$\varepsilon\mathrm{\text{-}{\TeX}}$$ Sổ tay, một đặc điểm quan trọng của *biểu thức* là việc đánh giá (tính toán) của chúng không yêu cầu TeX thực hiện bất kỳ *phép gán nào*.

Trong thuật ngữ lập trình, phép gán là quá trình đặt (gán) một biến để có một giá trị cụ thể; ví dụ, gán `\count` ô đăng ký `99` để chứa giá trị `12345` qua `\count99=12345`. Nhiều kiểu gán khác diễn ra trong quá trình xử lý TeX—chẳng hạn gán các ô đăng ký token để chứa một dãy token, gán các ô đăng ký hộp để chứa nội dung hộp, v.v.

Để thực hiện một phép gán, chẳng hạn như `\count99=12345`, TeX cần kích hoạt (thực thi) mã nội bộ triển khai hành vi của `\count` hoặc bất kỳ primitive nào khác thực hiện một kiểu gán nào đó. Tuy nhiên, có những lúc TeX đang thực hiện thuần túy *mở rộng* và, vào những lúc đó, các phép gán như vậy sẽ không được kích hoạt—*tại thời điểm đó trong quá trình xử lý của TeX*. Các ví dụ cho tình huống này bao gồm các lệnh sau:

* `\edef\command {*token list*}` lệnh định nghĩa macro “định nghĩa mở rộng” mở rộng các token trong *danh sách token* và lưu kết quả làm định nghĩa của `\command`.
* `\write *number* {*token list*}` mở rộng các token trong `*token list*` và ghi chúng ra một tệp được biểu thị bởi `*number*`.
* `\directlua {*token list*}` lệnh primitive LuaTeX này được dùng để chuyển mã Lua tới trình thông dịch Lua tích hợp. Tất cả các token trong `*token list*` được mở rộng hoàn toàn trước khi được chuyển tới trình thông dịch Lua để thực thi.

#### Ví dụ nhanh về \edef

Nếu ta viết các macro cơ bản sau:

```
     \def\mycount{\count99=12345}
     \edef\mymacro{\mycount}
```

`\edef` sẽ mở rộng `\mycount` thành các token cấu thành của nó nhưng không đi xa hơn: không có lệnh nào chứa trong định nghĩa của `\mymacro` sẽ được kích hoạt: tức là, phép gán của `12345` sang `\count99` *không xảy ra ở thời điểm này*; chỉ khi ta gọi `\mymacro` thì phép gán đó mới diễn ra khi TeX thực thi mã để xử lý primitive `\count` . Khi TeX đang thực hiện *chỉ các hoạt động mở rộng* thì mọi phép gán sẽ được kích hoạt sau trong quá trình xử lý của TeX, chứ không phải ngay trong quá trình mở rộng.

#### Vì sao các phép gán lại đáng quan tâm ở đây?

Khi viết mã để thực hiện một vòng lặp—trong bất kỳ ngôn ngữ lập trình nào—thông lệ là có một biến được chỉ định để làm “bộ đếm vòng lặp”: dùng để kiểm soát số lần một vòng lặp được thực thi. Việc lặp thường được điều khiển bằng cách kiểm tra xem biến bộ đếm vòng lặp được chỉ định đó đã đạt đến một giá trị cụ thể hay chưa—biến đó được tăng (hoặc giảm) cho mỗi lần lặp của vòng. Tuy nhiên, việc sửa đổi một biến bộ đếm vòng lặp nghĩa là gán cho nó một giá trị mới, mà đối với TeX, thường đòi hỏi lệnh primitive `\advance` để tăng (hoặc giảm) một giá trị được lưu trong một `\count` ô đăng ký. Như ta đã thấy, trong quá trình mở rộng thuần túy của TeX, các phép gán như vậy (bao gồm cả việc tăng biến) không thể diễn ra: macro `\foo` khéo léo vượt qua hạn chế này.

### Quay lại giải thích \foo

Macro `\foo` có thể kiểm soát quá trình lặp *không có* mà không cần gán giá trị cho bất kỳ biến nào: nó kiểm soát tần suất vòng lặp diễn ra bằng dữ liệu phát sinh từ quá trình mở rộng: các giá trị dữ liệu được lưu trong các danh sách token tạm thời. Dựa trên hiểu biết của chúng ta về việc TeX sử dụng (tạo ra) các danh sách token tạm thời, ta có thể xem kỹ hơn để thấy chính xác cách `\foo` đạt được kết quả của nó.

**Nhớ**: Chúng ta đang lần theo quá trình thực thi của một macro sau khi văn bản gốc của định nghĩa nó—được chứa trong một `.tex` tệp—đã được quét (đọc vào bởi TeX) và chuyển thành một danh sách token biểu diễn định nghĩa macro. Về cơ bản, chúng ta đang theo dõi quá trình TeX xử lý những *token* đã lưu trữ đó trong khi nó đọc và xử lý các token trong định nghĩa macro nằm ở đâu đó trong bộ nhớ của TeX. Bất kỳ ký tự khoảng trắng nào vốn có trong mã TeX của định nghĩa macro (văn bản bên trong `.tex` tệp) sẽ đã bị hấp thụ trong khi TeX quét văn bản đó để tìm lệnh (khoảng trắng làm dấu kết thúc), hoặc chúng sẽ đã được chuyển thành token, chẳng hạn như ký tự khoảng trắng sau dấu phẩy (`,`) trong `\ifnum#1<#2,` mà phát sinh từ việc chuyển đổi ký tự kết thúc dòng (`\r`) thành một khoảng trắng.

Vì mã TeX trong `\foo` sử dụng nhiều `\expandafter` lệnh, chúng ta sẽ hỗ trợ phần giải thích bằng cách thêm chỉ số dưới vào mỗi `\expandafter`, để chỉ rõ chúng ta đang nói đến lệnh nào. Ngoài ra, chúng ta sẽ mở rộng ký hiệu cho các token được xử lý bởi `\expandafter` sang $$\mathrm{T^i\_1}$$ và $$\mathrm{T^i\_2}$$, biểu diễn các token $$\mathrm{T\_1}$$ và $$\mathrm{T\_2}$$ cho `\expandafter<sub>i</sub>`: `\expandafter<sub>i</sub>` $$\mathrm{T^i\_1T^i\_2}$$

Đây là mã macro có chú thích:

```
    \def\foo#1#2{\number#1
    \ifnum#1<#2,
    \expandafter1\foo
    \expandafter2{\number\numexpr#1+1\expandafter3}%
    \expandafter4{\number#2\expandafter5}%
    \fi}
```

`\foo` bắt đầu bằng `\number#1` được mở rộng bằng lệnh có khả năng mở rộng `\number` để chuyển giá trị đối số thứ nhất thành dạng dàn trang của nó. Lệnh `\number` hoạt động bằng cách tạo ra một danh sách token tạm thời chứa các token ký tự biểu diễn từng chữ số riêng lẻ có trong giá trị số mà `\number` đang xử lý. Danh sách token đó trở thành nguồn nhập tiếp theo của TeX. Ở đây, danh sách token đó được đọc và các token được xuất ra để dàn trang giá trị của `#1`.

Tiếp theo, macro thực hiện phép kiểm tra `\ifnum#1<#2` để kiểm tra xem đối số cho `#1` có nhỏ hơn đối số truyền vào cho `#2`. Nếu đúng như vậy, một token dấu phẩy (`,`) sẽ được xuất (dàn trang), theo sau bởi một khoảng trắng phát sinh từ token được tạo ra từ ký tự xuống dòng sau dấu phẩy (`,`). Ký tự khoảng trắng đó ban đầu được tạo ra khi TeX đọc dòng này từ `.tex` .

Macro tiếp tục xử lý phần mã tiếp theo này, vốn là phần cốt lõi trong hoạt động của nó:

```
    \expandafter1\foo
    \expandafter2{\number\numexpr#1+1\expandafter3}%
    \expandafter4{\number#2\expandafter5}%
    \fi}
```

Về cơ bản, mã này tạo ra một chuỗi các danh sách token tạm thời, dẫn đến nhiều lần gọi `\foo` macro, kết thúc khi phép kiểm tra if `\ifnum#1<#2` không còn đúng nữa. Nhưng *cách* việc lặp được điều khiển như thế nào khi không có phép gán nào diễn ra: “bộ đếm vòng lặp” ở đâu?

Hãy bắt đầu bằng cách xem xét mã `\expandafter<sub>1</sub>\foo\expandafter<sub>2</sub>`. Lưu ý rằng chúng ta sẽ dùng ký hiệu chỉ số dưới `<sub>token</sub>` (hoặc `<sub>(token)</sub>`) để nhắc chúng ta rằng, ở đây, TeX đang đọc/xử lý các giá trị token số (số nguyên).

Ở đây, ta có các token sau làm đầu vào cho `\expandafter<sub>1</sub>`:

* $$\mathrm{T^1\_1} =$$`\foo<sub>token</sub>` được đọc vào và lưu lại để sau *việc chèn lại* trở lại đầu vào
* $$\mathrm{T^1\_2} =$$`\expandafter<sub>2 (token)</sub>` được mở rộng

Đối với `\expandafter<sub>2</sub>` ta có:

* $$\mathrm{T^2\_1} =$$`{<sub>token</sub>` thứ được lưu lại để dùng sau *việc chèn lại* trở lại đầu vào
* $$\mathrm{T^2\_2} =$$ `\number<sub>token</sub>` được mở rộng

**Lưu ý:**`\number` là một lệnh có khả năng mở rộng với mục đích “chuyển thành token”: tức là chuyển một đại lượng số thành một chuỗi các token ký tự biểu diễn đại lượng đó. Khi `\number` được mở rộng, việc đầu tiên mà TeX làm là quét đầu vào để tìm các số nguyên: một quá trình kích hoạt việc mở rộng tiếp theo.

**Mấu chốt của câu chuyện:** Ở đây, `\number` đang tác động lên *biểu thức* `\numexpr#1+1` tính giá trị của `#1+1`. Kết quả của phép tính đó được xử lý bởi `\number` để chuyển nó thành một danh sách token tạm thời chứa các token ký tự biểu diễn giá trị của `#1 + 1`. Danh sách token tạm thời đó, được tạo bởi `\number`, cuối cùng sẽ được đọc vào làm đối số thứ nhất cho một lần gọi khác của `\foo`. Thay vì tăng một bộ đếm vòng lặp (thông qua `\advance` và phép gán), việc dùng `\numexpr` tạo ra một giá trị mới mà không cần phép gán. Thông qua cơ chế này, biến điều khiển vòng lặp (`\foo`tham số của `#1`) được tăng lên và việc lặp qua vòng được điều khiển rồi kết thúc: thật là khéo léo!

Tiếp theo, `\expandafter<sub>3</sub>` được xử lý, cho ra:

* $$\mathrm{T^3\_1} =$$`}<sub>token</sub>` thứ được lưu lại để dùng sau *việc chèn lại* trở lại đầu vào
* $$\mathrm{T^3\_2} =$$`\expandafter<sub>4 (token)</sub>`, được mở rộng:

Đối với `\expandafter<sub>4</sub>` ta có:

* $$\mathrm{T^4\_1} =$$`{<sub>token</sub>` thứ được lưu lại để dùng sau *việc chèn lại* trở lại đầu vào
* $$\mathrm{T^4\_2} =$$`\number<sub>token</sub>` được mở rộng và chuyển `#2` thành một danh sách token tạm thời khác.

Cuối cùng,`\expandafter<sub>5</sub>` được mở rộng:

* $$\mathrm{T^5\_1} =$$`}<sub>token</sub>` thứ được lưu lại để dùng sau *việc chèn lại* trở lại đầu vào
* $$\mathrm{T^5\_2} =$$`\fi<sub>token</sub>`, là một lệnh có khả năng mở rộng.

  Việc mở rộng của `\fi` về thực chất chấm dứt `\ifnum` và, về mặt hiệu lực, khép lại lần lặp này của macro. TeX giờ hoàn tất việc chèn lại tất cả các token đã được lưu tạm bởi các `\expandafter` lệnh: việc này tạo ra một chuỗi các danh sách token một-token phát sinh từ các token được lưu bởi mỗi `\expandafter`. Ngoài ra TeX cũng đã tạo ra các danh sách token từ thông qua hành động của `\number`.

### Ghép các danh sách token

Về cơ bản, `\foo` macro tạo ra một chuỗi các danh sách token: bạn có thể xem `\foo` như một “cơ sở sản xuất” danh sách token. Những danh sách token đó được TeX đọc để trở thành các nguồn nhập tiếp theo. Phần khéo léo nằm trong một trong những hành động trước đó của `\foo`:

```
    \expandafter1\foo\expandafter2
```

qua đó `\foo` sắp xếp để tự gọi lại nhưng với các đối số khác được lưu trong các danh sách token do `\number`. Để khiến các danh sách token này cùng hoạt động như một lời gọi macro, các dấu ngoặc nhọn `{` và `}` đã được lưu và chèn lại vào đầu vào (dưới dạng các danh sách một-token) bởi các hành động của `\expandafter` lệnh.

![các danh sách token do macro \foo tạo ra](/files/614955599c21ce42c817e6fe3063eb6d22a55ce5)

&#x20;[Phần 1](/latex/vi/bai-viet-chuyen-sau/19-how-does-expandafter-work-an-introduction-to-tex-tokens.md)   [Phần 2](/latex/vi/bai-viet-chuyen-sau/22-how-does-expandafter-work-the-meaning-of-expansion.md)   [Phần 3](/latex/vi/bai-viet-chuyen-sau/21-how-does-expandafter-work-tex-uses-temporary-token-lists.md)   [Phần 4](/latex/vi/bai-viet-chuyen-sau/20-how-does-expandafter-work-from-basic-principles-to-exploring-tex-s-source-code.md)   [Phần 5](/latex/vi/bai-viet-chuyen-sau/17-how-does-expandafter-work-a-detailed-macro-case-study.md)   [Phần 6](/latex/vi/bai-viet-chuyen-sau/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/vi/bai-viet-chuyen-sau/17-how-does-expandafter-work-a-detailed-macro-case-study.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.
