> 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/22-how-tex-macros-actually-work-part-4.md).

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

[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

Trong Phần 1–3, chúng ta đã đi sâu vào một số chi tiết nền tảng ở mức thấp để chuẩn bị cho việc hiểu cách các macro TeX hoạt động. Trong bài viết này, chúng ta sẽ “ngoi lên hít thở” một chút để ôn lại một số nguyên tắc thực hành cơ bản của macro TeX, sẵn sàng cho một lần đào sâu nữa ở Phần 5 và 6. Trong suốt bài viết này, chúng ta sẽ trình bày các macro ví dụ được định nghĩa bằng lệnh nguyên thủy TeX `\def`: chúng ta sẽ không dùng lệnh LaTeX có lẽ quen thuộc hơn `\newcommand`. Có một lý do rất chính đáng cho việc này: mục tiêu của chúng ta là hiểu các nguyên tắc nền tảng chi phối hành vi macro của TeX, nhưng để làm vậy chúng ta cần dùng các lệnh lõi *được tích hợp sẵn trong* phần mềm TeX. Các lệnh LaTeX, chẳng hạn như `\newcommand`, bản thân chúng là các macro: những lệnh có hành vi được lập trình cụ thể, và rốt cuộc được cấu thành từ nhiều lớp lệnh nguyên thủy TeX ở mức thấp hơn. Để hiểu rõ hơn hành vi nền tảng của TeX, chúng ta phải dùng các nguyên thủy TeX, không phải các macro LaTeX.

### Chúng ta đang hướng tới đâu?

Về bản chất, chúng ta đang hướng tới lời giải thích về macro như một dạng đặc biệt của danh sách token: khi bạn yêu cầu TeX định nghĩa một macro, nó tạo ra một chuỗi token (một danh sách token) và lưu chúng trong bộ nhớ của nó, gắn với một tên do bạn định nghĩa. Ở Phần 5 và 6, chúng ta sẽ xem xét chi tiết hơn các danh sách token của macro, nhưng nếu bạn muốn rời khỏi bài viết này để đọc một số thông tin nền, bạn có thể tìm thấy điều đó trong [Danh sách token TeX là gì?](/latex/vi/bai-viet-chuyen-sau/54-what-is-a-tex-token-list.md)

Các danh sách token cho macro, và các tham số của chúng, bao gồm một vài điểm bổ sung mà chúng ta sẽ phân tích chi tiết — và dùng rất nhiều ví dụ. Để hiểu rõ hơn hành vi xử lý macro của TeX, thực sự chỉ có một yếu tố then chốt cần nhớ: TeX chỉ “nghĩ” về ký tự ở giai đoạn sớm nhất của quá trình xử lý đầu vào; từ đó trở đi thì chỉ còn token mà thôi! Trong bài này và các bài còn lại trong loạt bài này, chúng ta sẽ khám phá vai trò mà token đóng trong các macro TeX.

## Bốn phần của một macro

### Nhắc lại: tham số macro và đối số macro

Trước khi bắt đầu, cũng đáng để nhắc lại sự khác nhau giữa *tham số* và *đối số*đối số của một macro, vì chúng ta sẽ dùng cả hai thuật ngữ trong suốt bài viết này. Giả sử bạn định nghĩa một macro \foo

```
\def\foo#1#2{Đây là #1, đây là #2}
```

Các cấu trúc (token) `#1`, `#2` (tới `#9`) được gọi là *tham số*: hãy xem chúng như các “chỗ giữ chỗ” cho dữ liệu thực mà bạn sẽ dùng khi gọi macro:

```
\foo{alpha}{beta}
```

Ở đây, `alpha` và `beta` là các *đối số* được dùng với lần gọi \foo này: đối số của một macro là các giá trị thực được đưa vào các chỗ giữ chỗ tham số (`#1`, `#2`... `#9`) được dùng khi bạn định nghĩa macro.

### Định nghĩa macro: có 4 thành phần

Chúng ta sẽ bắt đầu bằng một định nghĩa về macro có lẽ trông hơi trang trọng, nhưng nó cung cấp một khuôn khổ hữu ích cho các thảo luận tiếp theo.

Bất kỳ định nghĩa macro nào cũng gồm 4 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 đặt cho lệnh macro của bạn, chẳng hạn như `\foo`;
* `<parameter text>`: phần này có thể không có, nhưng nếu có thì nó là một chuỗi *token* xuất hiện trước `<replacement text>` của macro.  `<parameter text>` có thể chứa các tham số macro (`#1`, `#2`... `#9`) và các loại token khác. Về bản chất, và như chúng ta sẽ thấy chi tiết,  `<parameter text>` cung cấp một kiểu *mẫu token* mà TeX dùng để xác định những token nào người dùng muốn dùng làm đối số của macro: khi bạn gọi một macro, TeX sẽ đưa các *lập thành một* token vào `<replacement text>` (cũng là một danh sách token);
* `{<replacement text>}`: đây là phần thân thực sự của macro của bạn: đó là một chuỗi token mà vào đó các *đối số* đối số được “chèn” vào khi macro được xử lý (mở rộng). Các đối số được đưa vào các vị trí được chỉ ra bởi các tham số macro dùng trong định nghĩa ban đầu.

**GHI CHÚ**: Trong suốt phần thảo luận, chúng ta giả định rằng `<macro name>` sẽ được theo sau bởi một ký tự khoảng trắng có mã danh mục 10 để làm dấu phân tách kết thúc `<macro name>`Chúng ta *không* trình bày rõ ký tự khoảng trắng đó trong văn bản/thảo luận của chúng ta nhưng chúng ta giả định là nó có ở đó. Nói chính xác, ta nên biểu diễn nó như thế này:

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

Tuy nhiên, chúng ta sẽ lược bỏ việc đưa rõ ràng một `<space>` ký tự và ngầm định nó có mặt.

#### Ghi chú về {}

Chúng ta đã cho thấy việc dùng hai dấu ngoặc nhọn: `{` và `}` chúng bao quanh (giới hạn) phần thân thực sự của `<replacement text>`macro của bạn. Tuy nhiên, việc dùng `{` và `}` chỉ là một quy ước được chấp nhận vì thực ra TeX mong đợi văn bản của macro `<replacement text>` bắt đầu bằng một ký tự có mã danh mục 1 (“Bắt đầu một nhóm”) và kết thúc bằng một ký tự có mã danh mục 2 (“Kết thúc một nhóm”). Theo quy ước, đó là các `{` và `}` ký tự tương ứng. Tuy nhiên, nếu muốn, bạn có thể gán bất kỳ cặp ký tự nào để làm việc đó. Ví dụ

```
\catcode`\(=1
\catcode`\)=2
```

Bây giờ, bạn có thể định nghĩa và dùng macro như thế này:

```
\def\foo #1(Hello, #1)
\foo(World!)
```

Và bạn vẫn có thể dùng `{` và `}` vì chúng ta chưa thay đổi mã danh mục của chúng — nhiều ký tự có thể, và thực tế có, cùng chung một mã danh mục, nên bạn vẫn có thể tiếp tục định nghĩa macro theo cách thông thường:

```
\def\foo #1{Hello, #1}
\foo{World!}
```

### và {}

Các thành phần trong định nghĩa macro liên quan nhất đến cuộc thảo luận của chúng ta là `<parameter text>` và `{<replacement text>}`. Khi bạn định nghĩa một macro,  `<parameter text>` thực chất là một mẫu token nghiêm ngặt xác định cách macro sẽ được sử dụng. Như đã lưu ý, `<parameter text>` có thể để trống, ví dụ, `\def\foo{Some text}` khi không có gì xuất hiện giữa tên lệnh của chúng ta (`\foo`) và dấu ngoặc nhọn mở `{` nó, ở đây, báo hiệu phần bắt đầu của `<replacement text>`.

**Lưu ý**: Một số độc giả có thể biết về cơ chế “hashquote” của TeX (`#{`) nhưng chúng ta sẽ không đề cập đến điều đó ở đây.

### và dấu phân tách macro

Bài viết cụ thể này không nhằm trở thành một bài tổng quan thật sự toàn diện về cách viết macro, nhưng đáng để có một phần ôn lại ngắn, với vài ví dụ, cho thấy rằng `<parameter text>` có thể trở nên phức tạp vì TeX cho phép `<parameter text>` chứa:

* **tham số macro**: (`#1`, `#2`,... `#9`) đóng vai trò là “chỗ giữ chỗ” cho các giá trị mà người dùng sẽ cung cấp khi họ chạy macro — *đối số*;
* **các token phân tách**: các token tùy ý, xen kẽ trong/bao quanh các token tham số, và được dùng để xác định ranh giới giữa các tham số macro. Bạn có thể xem các dấu phân tách đó như một dạng “dấu câu” tạo nên `<parameter text>` một “mẫu token” mà bạn sẽ cần theo khi dùng macro. Các token phân tách không được dàn trang.

Vậy điều này thực sự có nghĩa gì — hãy xem vài ví dụ.  `<parameter text>` nằm giữa tên macro và dấu ngoặc nhọn trái `{` trong định nghĩa của macro.

#### Dấu phân tách: Ví dụ 1

Giả sử chúng ta định nghĩa một macro cơ bản `\foo` như sau:

```
\def\foo ABC{xin chào, ở đó}
```

* `<parameter text>` = `ABC`
* Không có token tham số (`#1`, `#2`,... `#9`)
* Các dấu phân tách là *ba token ký tự* `ABC` nhưng trong ví dụ này, chúng hơi thừa và chỉ được dùng làm ví dụ.

Ba token ký tự `ABC` được xem như các dấu phân tách: không phải thứ để dàn trang mà là “dấu câu” được kỳ vọng sẽ có mặt khi macro được gọi — khi bạn dùng macro `\foo` bạn phải cung cấp đúng các dấu phân tách đã có khi nó được định nghĩa.

Nếu bạn gõ `\foo ABC` trong văn bản của bạn thì nó sẽ dàn trang `xin chào, ở đó`—các token ký tự `ABC` không được dàn trang nhưng TeX đã kiểm tra rất cẩn thận để chắc chắn rằng chúng có mặt trong lần gọi (invocation) của bạn đối với `\foo`. TeX tìm các dấu phân tách đó (“dấu câu”) và loại bỏ (hấp thụ) chúng như bạn có thể thấy trong ảnh màn hình Overleaf sau: `ABC` không được dàn trang:

![Overleaf đang chạy một macro TeX](/files/30acba52095023841f79c71fda37881e16068d3a)

Nếu bạn cố dùng `\foo` không có `ABC` các dấu phân tách, TeX sẽ báo lỗi `Việc dùng \foo không khớp với định nghĩa của nó.`:

![Hiển thị lỗi TeX trên Overleaf](/files/dd9aa71c50482ba1aa83e279968bc7786e64413d)

Trong ví dụ trên, sau khi phát hiện `\foo` TeX mong đợi thấy ba token ký tự `ABC` nhưng nó không thấy: nó thấy token ký tự của `w` và ngay lập tức phát hiện có điều gì đó không đúng.

#### Dấu phân tách: Ví dụ 2

Một तथ्य thú vị, và có lẽ gây ngạc nhiên, là các dấu phân tách có thể là các token lệnh tùy ý — kể cả những lệnh chưa *thậm chí chưa được định nghĩa*. Ví dụ, chúng ta có thể định nghĩa `\foo` như sau:

```
\def\foo A\bob B\anne{Xin chào \TeX{}}
```

* `<parameter text>` = `A\bob B\anne`
* Không có token tham số (`#1`, `#2`,... `#9`)
* Các dấu phân tách là các token `A\bob B\anne` nhưng một lần nữa, trong ví dụ này, chúng là thừa và chỉ nhằm mục đích thảo luận. Lưu ý rằng `\bob` và `\anne` là các lệnh hư cấu đã *không được định nghĩa*—và cũng không cần phải được định nghĩa.

Khi bạn gọi một macro, TeX sẽ kiểm tra (quét)  `<parameter text>` đang hiện diện *trong lời gọi macro của bạn* và so sánh nó, token theo token, với phiên bản “mẫu token” *được lưu trong bộ nhớ*—phiên bản được tạo ra vào lúc macro được định nghĩa. TeX quét *được dùng trong lời gọi macro của bạn* và chỉ đơn giản chuyển mọi lệnh nó tìm thấy ở đó thành giá trị token số của chúng: nó không cố thực thi các lệnh đó, vì vậy việc `\bob` và `\anne` chưa bao giờ được định nghĩa là không quan trọng. TeX chỉ đơn giản dùng mẫu token lưu trong bộ nhớ như một hướng dẫn để giúp nó xác định token nào trong `<parameter text>` của lời gọi macro của bạn là các đối số thực sự cần đưa vào `<replacement text>`.

Như bạn có thể thấy từ đoạn ảnh màn hình Overleaf sau, không token ký tự nào trong số (`Một` và `B`) bên trong `A\bob B\anne` được dàn trang và các lệnh chưa định nghĩa của chúng ta `\bob` và `\anne` không gây ra vấn đề gì. Tất cả các token này đã được *hấp thụ* bởi TeX khi nó đối chiếu cách bạn dùng `\foo` với định nghĩa (mẫu token) của `\foo` (một danh sách token) được lưu trong bộ nhớ.

![Overleaf đang chạy một macro TeX](/files/df4902484d99a8fe1943ffff5825f7df94ca4b1e)

#### Dấu phân tách: Ví dụ 3

Bạn có thể xen kẽ nhiều dấu phân tách khác nhau (token ký tự, token lệnh) với các tham số macro, như trong ví dụ này:

```
\def\foo A\bob#1B\anne#2\jane#3bye!{Xin chào từ \TeX{} tới \#1=#1, \#2=#2 và \#3=#3}
```

* `<parameter text>` = `A\bob#1B\anne#2\jane#3bye!`
* Có 3 token tham số macro: `#1`, `#2`, `#3`
* Lần này, các tham số (`#1`, `#2`, `#3`) được phân tách bởi sự kết hợp của các token:

  `A\bob#1B\anne#2\jane#3bye!`

Ở đây, thực chất là bạn đã cung cấp cho TeX một mẫu mà nó sẽ rất cẩn thận cố gắng, và kỳ vọng, khớp lại khi bạn gọi `\foo`: nó sẽ đi qua lời gọi macro của bạn, *token theo token*, và mong khớp (tìm thấy):

* hai token `Một` và `\bob` trước khi `#1`
* hai token `B` và `\anne` trước khi `#2`
* token `\jane` trước tham số `#3` và *bốn token ký tự* `b`, `y`,`e` và `!` sau `#3`.

**Nhớ**: TeX nghĩ bằng token nên `bye!` là *bốn token ký tự*.

Có hai cách để dùng macro này — chúng ta có thể đặt bất kỳ đối số nhiều token nào trong dấu ngoặc nhọn để tạo thành một nhóm:

```
\foo A\bob{Này}B\anne{Đó}\jane{Khác}bye!
```

Tuy nhiên, điều đó *không cần thiết* vì định nghĩa macro của chúng ta có các dấu phân tách cung cấp một mẫu token. TeX có thể dùng mẫu đó để chọn ra các token cho từng đối số từ trong số các token chỉ là dấu phân tách. Chúng ta có thể dùng macro như thế này:

```
\foo A\bob NàyB\anne Đó\jane Khácbye!
```

và TeX có thể chọn ra các đối số để tạo ra kết quả giống như khi dùng các nhóm `{...}`:

![Overleaf đang chạy một macro TeX bằng cách dùng các dấu phân tách](/files/dbbe909e273d922df6c8c6e136cc851d876b4f97)

Lưu ý cách TeX có thể phát hiện chính xác token nào khớp với các tham số `#1` và `#3`:

![Overleaf đang chạy một macro TeX bằng cách dùng các dấu phân tách](/files/437b19f10861dfa0a1db945d0994547460364689)

#### Dấu phân tách: Ví dụ 4 (điều quan trọng là token, không phải ký tự!)

Dưới đây là một ví dụ ngắn để chứng minh rằng, khi làm việc với macro, điều quan trọng cần nhớ là chúng ta thực sự đang ở trong thế giới của *token* và **không** *ký tự*…

Chúng ta sẽ định nghĩa macro ngắn sau, trong đó A và B là các ký tự đóng vai trò dấu phân tách và cả hai đều có mã danh mục 11. Cách này hoạt động không vấn đề gì:

```
\documentclass{article}
\begin{document}
\def\foo A#1B{Hello, #1}
\foo AGrahamB
\end{document}
```

và dàn trang `Xin chào, Graham`.

Tuy nhiên, nếu bạn đổi mã danh mục của A hoặc B sang giá trị khác 11, lời gọi macro sẽ thất bại. Giả sử chúng ta đổi mã danh mục của B thành 12, bằng cách dùng ``\catcode`B=12`` và thử gọi lại macro như trước:

```
\documentclass{article}
\begin{document}
\def\foo A#1B{Hello, #1}
\foo AGrahamB % Cách này hoạt động
\catcode`\B=12\relax
\foo AGrahamB
\end{document}
```

Nó thất bại với một lỗi khá khó hiểu:

```
Đối số bị trôi?
GrahamB \end {document}
! Tệp đã kết thúc trong khi quét việc dùng \foo.
<văn bản được chèn>
                \par
<*> main.tex

Tôi nghi là bạn đã quên một `}', khiến tôi
đọc quá chỗ bạn muốn tôi dừng.
Tôi sẽ cố phục hồi; nhưng nếu lỗi nghiêm trọng,
bạn nên gõ `E' hoặc `X' ngay bây giờ và sửa tệp của bạn.

! Dừng khẩn cấp.
<*> main.tex

*** (công việc bị hủy, không tìm thấy \end hợp lệ)
```

![Overleaf hiển thị lỗi macro TeX](/files/96837766a3e9715375b4c6ec092f25f8486805e0)

Tiếc là gợi ý mặc định của TeX rằng ``Tôi nghi là bạn đã quên một `}'`` là **không chính xác**, nó không phải do thiếu một dấu ngoặc nhọn (`}`).

#### Đã xảy ra chuyện gì?

TeX đang cố khớp `\foo` macro *lời gọi* với `\foo` macro *định nghĩa* nó đã lưu trong bộ nhớ. Khi bạn gọi một macro cần một hoặc nhiều đối số, TeX phải xem xét (quét) cách bạn dùng macro để xác định *đối số* đối số mà bạn đang cung cấp cho macro đó. Ở đây, TeX đang mong đợi đối số nằm kẹp giữa một A (mã danh mục 11) và một B (mã danh mục 11). Hãy nhớ: TeX đang nghĩ bằng *token*, **không** *ký tự*.

Giá trị token của A và B, $$\mathrm{T\_A}$$ và $$\mathrm{T\_B}$$ lần lượt là:

$$\mathrm{T\_A = 256 \times 11 + 65 = 2881}$$ $$\mathrm{T\_B = 256 \times 11 + 66 =2882}$$

Tuy nhiên, khi TeX tiếp tục tìm kiếm, nó thấy B *nhưng* bây giờ nó có mã danh mục 12 và điều đó dẫn đến một giá trị token khác:

$$\mathrm{T'\_B= 256 \times 12 + 66 =3138}$$

Trong `\foo` định nghĩa macro, giá trị token của B (được dùng làm dấu phân tách) là 2882 nhưng giờ TeX thấy một giá trị token là 3138: nó nghĩ đây chỉ là một token khác sẽ được dùng trong *lập thành một* được cung cấp cho `\foo`. Theo TeX, token cuối cùng của đối số vẫn chưa được tìm thấy nên nó đi lấy thêm một token khác trong quá trình tìm một B có mã danh mục 11. Đó là lúc, và là lý do, macro thất bại: trong nỗ lực tìm đối số, TeX “đi quá đà” và bắt đầu đọc những token mà bạn không hề định cho chúng trở thành một phần của đối số cho `\foo` lời gọi macro. Điều tiếp theo xảy ra phụ thuộc vào các token mà TeX phát hiện sau B — chúng sẽ kích hoạt các `Đối số bị trôi?` lỗi khác nhau.

Trong ví dụ của chúng ta, TeX đi quá đà và tiêu thụ luồng token `\end {document}` và nhanh chóng chạm tới cuối tệp, vì thế thông báo lỗi:

```
! Tệp đã kết thúc trong khi quét việc dùng \foo
```

## Phần 5

Như chúng ta đã thấy ở trên, phần `<parameter text>` trong định nghĩa macro của chúng ta có thể từ cực kỳ đơn giản cho đến một sự pha trộn phức tạp giữa các tham số macro xen kẽ với các token ký tự và token lệnh đóng vai trò dấu phân tách. TeX có thể xử lý các tổ hợp token khác nhau hiện diện trong `<parameter text>` để trích xuất các đối số được cung cấp cho một macro — và phát hiện khi nào chúng ta cố dùng một macro sai cách. Cách nó làm điều đó là chủ đề của bài tiếp theo trong loạt bài này.

[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/22-how-tex-macros-actually-work-part-4.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.
