> 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/47-tex-tables-how-tex-calculates-spanned-column-widths.md).

# Bảng TeX: Cách TeX tính toán độ rộng cột gộp

## Mục tiêu của bài viết này

Trong bài viết này, chúng ta tìm hiểu cách $$\mathrm\TeX$$ tính toán độ rộng các cột của bảng khi bảng chứa các mục (ví dụ: tiêu đề bảng) trải rộng qua nhiều cột (ví dụ: bằng cách dùng $$\mathrm\TeX$$ các nguyên thủy `\omit` và `\span`). Sử dụng một [bảng “tham chiếu”](#reference-table) làm điểm khởi đầu, chúng ta tạo ra một loạt ví dụ—được suy ra từ bảng tham chiếu đó—bằng cách sửa đổi các mục khác nhau để tạo ra các cột gộp. Bằng cách xem xét tác động của những thay đổi đó, chúng ta có thể bắt đầu hình thành sự hiểu biết về thuật toán nền tảng mà $$\mathrm\TeX$$ dùng để tính độ rộng của các cột gộp.

### Sử dụng $$\mathrm\TeX$$ không $$\mathrm\LaTeX$$

Để khảo sát và giải thích *cách* $$\mathrm\TeX$$ quyết định độ rộng của các cột gộp, cần phải gạt bỏ tất cả những $$\mathrm\LaTeX$$ gói bảng tuyệt vời và quay trở lại các lệnh tạo bảng cơ bản, ở mức thấp (nguyên thủy): đặc biệt là `\halign{...}`, `\span` và `\omit`. [Các $$\mathrm\TeX$$/$$\mathrm\LaTeX$$ gói bảng](https://ctan.org/topic/table) đương nhiên là những công cụ năng suất thiết yếu và cung cấp rất nhiều chức năng cực kỳ hữu ích, cho phép người dùng nhanh chóng tạo ra vô số nội dung dạng bảng bằng $$\mathrm\LaTeX$$. Những gói đó cung cấp “khung macro” thiết yếu được xây dựng xung quanh $$\mathrm\TeX$$hành vi ở mức thấp của nó, và các nhà phát triển của chúng cung cấp những lớp trừu tượng hóa và lớp bảo vệ rất đáng hoan nghênh, giúp xử lý các phức tạp nền tảng. Nhiều gói trong số đó thực sự là những thành tựu đáng kinh ngạc của $$\mathrm\TeX$$ lập trình phức tạp: tất cả chúng ta nên biết ơn vì chúng tồn tại để che chắn chúng ta khỏi việc phải dùng $$\mathrm\TeX$$!

Thuật toán thực sự mà $$\mathrm\TeX$$ dùng để tính độ rộng các cột gộp được giải thích ở trang 245 của [$$\mathrm\TeX\text{book}$$](https://www.amazon.co.uk/TeXbook-Donald-E-Knuth/dp/0201134489) và, với thêm chi tiết, ở Mục 801 (trang 336) của cuốn sách in chứa $$\mathrm\TeX$$mã nguồn của [$$\mathrm\TeX\text{: The Program}$$](https://www.amazon.co.uk/Computers-Typesetting-Tex-Program-TEX/dp/0201134373). Tuy nhiên, đối với nhiều người (trong đó có tôi), các giải thích của Knuth đôi khi khá ngắn gọn, cô đọng và, có lúc, khó theo dõi chi tiết: các ví dụ minh họa luôn rất hữu ích.

### Đúng vậy, bảng biểu rất phức tạp

Trong mục 768 (trang 322) của cuốn sách [$$\mathrm\TeX\text{: The Program}$$](https://www.amazon.co.uk/Computers-Typesetting-Tex-Program-TEX/dp/0201134373), Knuth có một nhận xét thú vị:

> “Thật gần như là một phép màu mỗi khi `\halign` và `\valign` hoạt động, vì chúng cắt ngang qua rất nhiều cấu trúc điều khiển của $$\mathrm\TeX$$.”

Ngoài ra, Tập IV của bộ sách bốn tập [$$\mathrm\TeX\text{ in Practice}$$](https://www.amazon.co.uk/Tex-Practice-Set-Stephan-Bechtolsheim/dp/038797296X/ref=sr_1_11?s=books\&ie=UTF8\&qid=1504256043\&sr=1-11\&keywords=TeX+in+Practice) dành không ít hơn 180 trang (tr. 199–379) cho việc tạo bảng trong $$\mathrm\TeX$$ qua `\halign` và `\valign`.

Vì vậy, có thể khẳng định rằng $$\mathrm\TeX$$ bảng biểu quả thật “khá rắc rối”.

### Gộp cột: \omit, \span và \multispan

Như đã lưu ý, để khám phá $$\mathrm\TeX$$việc tính độ rộng cột của chúng ta cần dùng “thô” $$\mathrm\TeX$$; điều này có nghĩa là kết hợp các lệnh nguyên thủy và một $$\text{Plain }\mathrm\TeX$$ macro có tên là `\multispan`. Mặc dù chúng ta sẽ không dùng các lệnh này để minh họa trực tiếp các bảng ví dụ của mình (tức là, giải thích đầy đủ tất cả $$\mathrm\TeX$$ mã), nhưng cũng đáng để đưa vào một ghi chú ngắn giải thích chúng:

* `\halign`: Một trong hai $$\mathrm\TeX$$ lệnh nguyên thủy (command) để tạo bảng. Lệnh còn lại là `\valign` nhưng lệnh đó không được dùng rộng rãi bằng và sẽ không được bàn đến trong bài viết này.
* `\omit`: Một $$\mathrm\TeX$$ lệnh nguyên thủy hướng dẫn $$\mathrm\TeX$$ bỏ qua mẫu tiền đề của một mục trong bảng.
* `\span`: Một $$\mathrm\TeX$$ lệnh nguyên thủy dùng để kết hợp hai mục bảng liền kề.
* `\multispan{n}`: Một $$\mathrm\TeX$$ macro đơn giản để gộp `n` các cột.

Về cơ bản, để gộp các cột $$\mathrm\TeX$$ bỏ qua số lượng mẫu tiền đề bảng phù hợp và kết hợp số lượng mục bảng cần thiết thành một mục duy nhất. `\multispan{n}` được mở rộng thành chuỗi các `\omit` và `\span` token cần để gộp `n` các cột. Ví dụ, `\multispan{3}` mở rộng thành `\omit\span\omit\span\omit`.

## Giới thiệu bảng “tham chiếu” của chúng ta

Đây là bảng tham chiếu của chúng ta, tiếp theo là một phiên bản có chú thích giải thích các thành phần được dùng để xây dựng nó:

![{{{alt}}}](/files/e2a3b519e4776fe9bc57cd25200ab466acf9b95e)

Bằng cách sửa đổi bảng tham chiếu, chúng ta sẽ quan sát điều gì xảy ra với độ rộng của bảng, và độ rộng của từng cột, khi chúng ta thêm các mục gộp qua nhiều cột khác nhau. Bảng tham chiếu này được tạo ra bằng $$\mathrm\TeX$$ sử dụng `\halign{...}` lệnh nguyên thủy cùng với một số macro tùy chỉnh cần thiết để dàn trang các bảng—chúng ta sẽ không bàn về những macro đó vì chúng không thiết yếu để hiểu các ví dụ và giải thích.

Đây là phiên bản có chú thích của bảng tham chiếu để giải thích các đặc điểm của nó:

![{{{alt}}}](/files/251682e0357168b56688a3b59ded6fef6ec01832)

Bộ bảng ví dụ đầu tiên của chúng ta, và bảng tham chiếu ban đầu, đều đặt `\tabskip=0pt` để $$\mathrm\TeX$$ không thêm khoảng trắng nào giữa các cột của chúng ta: thực chất, chúng đều chạm vào nhau. Lý do làm vậy là để đơn giản hóa phần thảo luận ban đầu và các phép tính tiếp theo— `\tabskip` glue khác 0 để xem tác động của nó lên việc tính độ rộng các cột gộp.

Như đã lưu ý trong phần chú thích, chúng ta đã thêm một lượng nhỏ khoảng trắng (5pt) ở đầu của tất cả các mục bảng không gộp (trừ hàng đầu tiên). Khoảng trắng 5pt đó là một phần của tổng độ rộng của tất cả các mục không gộp (trừ hàng đầu tiên) và chỉ được thêm vào để bảng trông bớt rối hơn một chút.

### Một ghi chú ngắn về độ rộng của bảng

Tính năng `\halign{...}` lệnh có ba dạng:

* `\halign{...}`: đặt bảng theo bất kỳ độ rộng nào $$\mathrm\TeX$$ tính toán, dựa trên kích thước của các mục (và `\tabskip` glue);
* `\halign to *width* {...}`: yêu cầu $$\mathrm\TeX$$ dàn trang bảng theo một `*độ rộng*`;
* `\halign spread *amount*{...}`: điều chỉnh độ rộng đã tính bằng `*lượng*`.

Khi $$\mathrm\TeX$$ dàn trang một bảng bằng `\halign{...}` nó phải đọc toàn bộ bảng vào bộ nhớ để thực hiện các phép tính cần thiết cho việc dàn trang. Do đó, trừ khi bạn đã chỉ định độ rộng bằng cách dùng `\halign to *width* {...}` bạn không thể biết độ rộng cuối cùng cho đến khi $$\mathrm\TeX$$ xử lý (dàn trang) xong nó. Một cách để lấy độ rộng của bảng do `\halign{...}` là trước hết dàn trang bảng bên trong một `\vbox{...}` (ví dụ, `\setbox0=\vbox{\halign{...}}`) rồi sau đó, chẳng hạn, dùng `\the\wd0` để lấy độ rộng.

### Không tự động ngắt dòng trong các mục bảng

Điều quan trọng cần lưu ý là khi $$\mathrm\TeX$$ đang dàn trang một bảng được tạo bằng `\halign{...}` bất kỳ văn bản nào trong các mục bảng cũng không tự động được ngắt dòng: các mục bảng được dàn trang ở *chế độ ngang hạn chế*—giống như một `\hbox`. Để bật ngắt dòng, văn bản của một mục bảng cần được đặt bên trong một `\vbox{...}` cùng với việc dùng một giá trị thích hợp cho `\hsize` trong đó `\vbox{...}`. Tuy nhiên, lưu ý rằng văn bản bên trong một `\noalign{...}` lệnh (một $$\mathrm\TeX$$ lệnh nguyên thủy) được dùng trong một `\halign{...}` có thể chịu $$\mathrm\TeX$$việc ngắt dòng của `\noalign{...}` cho phép $$\mathrm\TeX$$ “thoát” khỏi `\halign{...}` và đặt nội dung giữa các hàng của bảng—thường để tạo các đường kẻ ngang giữa các hàng.

### Không được phép: \halign{...} bên trong \hbox{...}

Bạn không thể *trực tiếp* dàn trang một `\halign{...}` bên trong một `\hbox{...}`. Cố gắng dùng `\hbox{\halign{...}}` sẽ tạo ra một lỗi khá khó hiểu:

```latex
! Thiếu } đã được chèn vào.
<văn bản được chèn>
                }
<to be read again>
                   \halign
l.1 \hbox{\halign
```

#### Giải thích lỗi này

Do bao quanh bởi `\hbox{...}` $$\mathrm\TeX$$ đang ở trong *chế độ ngang hạn chế*; sau đó nó phát hiện `\halign{...}` đó là một *chế độ dọc* lệnh. Ví dụ, nếu bạn dùng `\halign{...}` trong một đoạn văn, $$\mathrm\TeX$$ sẽ kết thúc đoạn văn, xử lý `\halign{...}` rồi sau đó tiếp tục với phần còn lại của đoạn văn.

Khi được dùng bên trong một `\hbox{...}`, còn `\halign{...}` kích hoạt $$\mathrm\TeX$$ cố gắng thoát trở lại chế độ dọc bằng cách ép đóng nhóm hiện tại: $$\mathrm\TeX$$ báo một “`! Thiếu }`” và đưa ra lỗi vì nó nghĩ rằng bạn đã mắc lỗi khi dùng nhóm. Mặc dù một dấu ngoặc nhọn bên phải (`}`) có thể không bị thiếu trong $$\mathrm\TeX$$ mã của bạn, thông báo lỗi là triệu chứng của `\hbox{...}` việc “chen ngang” của $$\mathrm\TeX$$ và nó đưa ra “đoán định tốt nhất” về cách xử lý thích hợp để giải quyết vấn đề.

## Các ví dụ về bảng có cột gộp

Chuỗi hình bảng sau đây cung cấp một loạt ví dụ để minh họa tác động của việc gộp cột bảng: cho thấy rằng các mục bảng dài có thể gây ra kết quả không ngờ lên độ rộng của một số cột—và, do đó, lên độ rộng của chính bảng. Câu hỏi chúng ta sẽ giải quyết là $$\mathrm\TeX$$ làm gì khi một mục bảng cụ thể trải qua nhiều cột nhưng lại “quá rộng để vừa”. Như đã lưu ý ở trên, $$\mathrm\TeX$$ thực sự áp dụng một thuật toán cụ thể cho bài toán tính độ rộng cột này: các ví dụ sau được thiết kế để giúp hình thành “cảm nhận” về cách hoạt động của thuật toán đó.

### Bảng ví dụ 1

Trong ví dụ này, chúng ta dùng `\multispan{2}` để gộp các cột 1 và 2 bằng một mục có văn bản là **Một tiêu đề bảng**:

![{{{alt}}}](/files/6c57e1c510b4cdda02f9fe9ca14900020608086f)

#### Quan sát

* Độ rộng của bảng này giống như [bảng tham chiếu](#reference-table): $$327.71722\text{pt}$$.
* Độ rộng của mục gộp các cột 1 và 2 là $$81.04953\text{pt}$$ nhỏ hơn tổng độ rộng của các mục trong những cột mà nó gộp: $$52.56676\text{pt} + 57.06679\text{pt} = 109.63355\text{pt}$$

## Bảng ví dụ 2

Như trong [Bảng ví dụ 1](#example-table-1), ví dụ này cũng dùng `\multispan{2}` để gộp các cột 1 và 2 nhưng ở đây chúng ta dùng một mục dài hơn có văn bản là **Một tiêu đề bảng dài hơn một chút**.

![{{{alt}}}](/files/3687343877faa5de1aabd24284768112bcaf842e)

#### Quan sát

Nếu bạn so sánh ví dụ này với [bảng tham chiếu](#reference-table) thì chúng ta có thể thấy như sau:

* Độ rộng của bảng này đã tăng từ $$327.71722\text{pt}$$ sang $$374.37032\text{pt}$$: tổng cộng $$46.6531\text{pt}$$.
* Độ rộng của mục gộp các cột 1 và 2 ($$156.28664\text{pt}$$) lớn hơn tổng độ rộng của các mục trong những cột mà nó gộp: $$52.56676\text{pt} + 57.06679\text{pt} = 109.63355\text{pt}$$. Phần chênh lệch đó là $$156.28664\text{pt}-109.63355\text{pt} = 46.6531\text{pt}$$ và đó cũng chính là mức mà độ rộng của bảng đã tăng lên.
* $$\mathrm\TeX$$ đã điều chỉnh độ rộng của cột 2 để cung cấp khoảng trống bổ sung cần thiết. Sau này chúng ta sẽ thấy cách $$\mathrm\TeX$$ tính ra mức mà cột 2 phải tăng.
* Cột 1 không bị ảnh hưởng: độ rộng của nó không bị tác động bởi mục gộp các cột 1 và 2.

### Bảng ví dụ 3

Trong ví dụ này, chúng ta dùng `\multispan{3}` để gộp các cột 1 đến 3 bằng một mục có văn bản giống như [Bảng ví dụ 2](#example-table-2): **Một tiêu đề bảng dài hơn một chút**.

![{{{alt}}}](/files/958e1a7fa3a7a2a1d5d5b74d14daba45266bf95c)

#### Quan sát

* Độ rộng của bảng này giống như [bảng tham chiếu](#reference-table): $$327.71722\text{pt}$$.
* Độ rộng của mục gộp các cột 1 đến 3 ($$156.28664\text{pt}$$) nhỏ hơn tổng độ rộng của các mục trong ba cột mà nó gộp: $$52.56676\text{pt} + 57.06679\text{pt} + 59.03899\text{pt} = 168.67254\text{pt}$$.
* Không có cột nào bị ảnh hưởng về độ rộng bởi mục gộp các cột 1 đến 3.

Bạn có bắt đầu thấy một khuôn mẫu đang hiện ra không?

### Bảng ví dụ 4

Cũng như với [Bảng ví dụ 3](#example-table-3), ở đây chúng ta dùng `\multispan{3}` để gộp các cột 1 đến 3 nhưng lần này bằng một mục có văn bản dài hơn đáng kể: **Một tiêu đề bảng dài hơn đáng kể, kéo dài rất xa**.

![{{{alt}}}](/files/466fec7f00956d09e5f11a7fe39091a81becc8c2)

#### Quan sát

* So với [bảng tham chiếu](#reference-table), độ rộng của bảng này đã tăng từ $$327.71722\text{pt}$$ sang $$465.95685\text{pt}$$: tăng thêm $$138.23963\text{pt}$$.
* Độ rộng của mục gộp các cột 1 đến 3 là $$306.91216\text{pt}$$.
* Tổng độ rộng của các mục trong ba cột được gộp là $$52.56676\text{pt} + 57.06679\text{pt} + 59.03899\text{pt} = 168.67254\text{pt}$$.
* Phần chênh lệch độ rộng giữa mục gộp dài và các mục trong các cột 1 đến 3 là $$306.91216\text{pt}-168.67254\text{pt}=138.23962\text{pt}$$. Chính bằng cùng một lượng (đến 4 chữ số thập phân!) mà độ rộng của bảng đã tăng.
* Chỉ cột 3 được tăng độ rộng: cả cột 1 lẫn cột 2 đều không bị ảnh hưởng.

#### Một khuôn mẫu xuất hiện

Nếu chúng ta nhìn vào [Bảng ví dụ 2](#example-table-2) và [Bảng ví dụ 4](#example-table-4) thì có thể thấy rằng trong cả hai trường hợp, đó là **cột cuối cùng trong phạm vi gộp** được tăng độ rộng để tạo chỗ cho mục dài gộp các cột:

* Trong [Bảng ví dụ 2](#example-table-2): Mục dài gộp các cột 1 và 2. Cột 2 trở nên “bị kéo giãn”.
* Trong [Bảng ví dụ 4](#example-table-4): Mục dài gộp các cột 1 đến 3. Cột 3 trở nên “bị kéo giãn”.

#### Độ rộng của cột 3: Một thuật toán xuất hiện?

Các phép tính sau cho thấy rõ hơn điều mà $$\mathrm\TeX$$ đang làm. Đây là những gì chúng ta biết:

* Độ rộng của mục dài gộp các cột 1 đến 3 là $$306.91216\text{pt}$$.
* Tổng độ rộng của các mục trong các cột 1 và 2 là $$52.56676\text{pt} + 57.06679\text{pt} = 109.63355\text{pt}$$.

Sự chênh lệch giữa các giá trị đó là gì? Đó là $$306.91216\text{pt}-109.63355\text{pt} = 197.2786\text{pt}$$ và đây là độ rộng được dùng cho cột 3: nó xuất phát trực tiếp từ thuật toán được $$\mathrm\TeX$$.

### Bảng ví dụ 5

Trước khi đến với một ví dụ phức tạp hơn, đây là thêm một ví dụ “đơn giản” nữa. Bảng này chứa cùng mục dài như [Bảng ví dụ 4](#example-table-4): **Một tiêu đề bảng dài hơn đáng kể, kéo dài rất xa**; tuy nhiên, lần này chúng ta dùng `\multispan{6}` điều này cho phép mục đó gộp toàn bộ bảng. Như bạn thấy, bảng kết quả vẫn có cùng độ rộng như bảng [bảng tham chiếu](#reference-table) ($$327.71722\text{pt}$$) có nghĩa là không có cột nào bị ảnh hưởng bởi mục rất dài này. Rõ ràng, điều đó là vì độ rộng của mục ($$306.91216\text{pt}$$) nhỏ hơn tổng độ rộng của tất cả các mục mà nó đang gộp: $$327.71722\text{pt}$$; tức là, độ rộng của bảng.

![{{{alt}}}](/files/ffe92853cad28828b19744edd0eb30b1e2164b96)

### Bảng ví dụ 6: Hơi phức tạp hơn

Ở đây, chúng ta xem xét một chuỗi ba bảng ví dụ (6(a)–6(c)) để cho thấy tác động của hai mục khác nhau, cả hai đều gộp tới cột 5. [Bảng ví dụ 6(a)](#example-table-6a) và [Bảng ví dụ 6(b)](#example-table-6b) đều cho thấy một bảng chứa một mục duy nhất gộp qua nhiều cột đến cột 5. [Bảng ví dụ 6(c)](#example-table-6c) kết hợp cả hai mục gộp vào một bảng duy nhất và đặt ra câu hỏi: mục nào thực sự quyết định độ rộng của cột 5, và tại sao? Câu trả lời đưa chúng ta đến cốt lõi của thuật toán được $$\mathrm\TeX$$.

#### Bảng ví dụ 6(a)

![{{{alt}}}](/files/f95eef8a0e89e07a3d0b57a6830d8205ca5c3df5)

**Quan sát**

* So với [bảng tham chiếu](#reference-table), độ rộng của bảng này đã tăng từ $$327.71722\text{pt}$$ sang $$371.11153\text{pt}$$: tăng thêm $$43.39431\text{pt}$$.
* Độ rộng của mục gộp các cột 3 đến 5 là $$215.06683\text{pt}$$.
* Tổng độ rộng của các mục trong các cột 3 đến 5 là $$59.03899\text{pt} + 52.98344\text{pt} + 59.6501\text{pt} = 171.67253\text{pt}$$.
* Phần chênh lệch độ rộng giữa các mục được gộp trong các cột 3 đến 5 và độ rộng của mục gộp là $$215.06683\text{pt}-171.67253\text{pt}=43.3943\text{pt}$$: chính xác bằng lượng (đến 4 chữ số thập phân!) mà độ rộng của bảng đã tăng.

#### Bảng ví dụ 6(b)

![{{{alt}}}](/files/d3dd5d925e7d3e3f5169ba4c99ffa71e577e8210)

**Quan sát**

* So với [bảng tham chiếu](#reference-table), độ rộng của bảng này đã tăng từ $$327.71722\text{pt}$$ sang $$353.3233\text{pt}$$: tăng thêm $$25.60608\text{pt}$$.
* Độ rộng của mục gộp các cột 1 đến 5 là $$306.91216\text{pt}$$.
* Tổng độ rộng của các mục trong các cột 1 đến 5 là $$52.56676\text{pt} + 57.06679\text{pt} + 59.03899\text{pt} + 52.98344\text{pt} + 59.6501\text{pt} = 281.30608\text{pt}$$.
* Phần chênh lệch độ rộng giữa các mục được gộp trong các cột 1 đến 5 và độ rộng của mục gộp là $$306.91216\text{pt}-281.30608\text{pt}=25.60608\text{pt}$$: **note** đây là *nhỏ hơn* so với giá trị được tính cho [Ví dụ 6(a)](#example-table-6a), vốn là $$43.3943\text{pt}$$.

#### Bảng ví dụ 6(c)

Ở đây, chúng ta kết hợp các mục trong các bảng ví dụ [6(a)](#example-table-6a) và [6(b)](#example-table-6b) vào một bảng duy nhất: điều gì xảy ra?

![{{{alt}}}](/files/b07f8f13187523b5eaa13baee6a21e0d291df7e9)

**Quan sát**

* So với [bảng tham chiếu](#reference-table), độ rộng của bảng này đã tăng từ $$327.71722\text{pt}$$ sang $$371.11153\text{pt}$$: tăng thêm $$43.39431\text{pt}$$. Chúng ta lưu ý điều này hoàn toàn giống như [Bảng ví dụ 6(a)](#example-table-6a).

#### Điều gì $$\mathrm\TeX$$ đang làm?

Để hiểu kết quả của $$\mathrm\TeX$$’s algorithm và các quá trình ra quyết định, chúng ta lưu ý rằng mục này

![{{{alt}}}](/files/4041d79d1f804274c0e5839022cf93cbca9a8c7a)

vượt ra ngoài các mục được gộp bởi $$25.60608\text{pt}$$; tuy nhiên, mục này

![{{{alt}}}](/files/e7c3c58b80bc541beacc9bcc307061a5a68866ff)

vượt xa hơn nữa so với các mục được gộp: đến $$43.3943\text{pt}$$. Vì vậy mục đó “thắng cuộc” và cột 5 được tăng độ rộng thêm **giá trị lớn nhất** trong hai giá trị này ($$43.3943\text{pt}$$). Độ rộng của cột 5 giờ trở thành $$59.6501\text{pt} + 43.3943\text{pt} = 103.0444\text{pt}$$ để chứa mục gộp các cột 3 đến 5. Phần mô tả “chuỗi sự kiện” chính xác của chúng ta được đơn giản hóa đôi chút nhưng kết quả thì đúng như đã mô tả.

## Đưa một chút phức tạp trở lại

Để giảm thiểu độ phức tạp của phần thảo luận (cho đến nay), chúng ta đã dùng các ví dụ tương đối đơn giản để minh họa các nguyên tắc của $$\mathrm\TeX$$’s algorithm; cụ thể, chúng ta đặt `\tabskip=0pt`. Trên thực tế, các bảng “thực tế” có khả năng có nhiều mục gộp qua một loạt cột và, dĩ nhiên, sẽ có các giá trị khác 0 cho `\tabskip` glue—một chủ đề mà giờ chúng ta sẽ xem lại.

### glue \tabskip và độ rộng các cột gộp

Thiết kế bảng thường đòi hỏi thêm khoảng trắng giữa các cột và, tất nhiên, $$\mathrm\TeX$$ có khả năng này thông qua một lệnh nguyên thủy có tên là `\tabskip`. Lệnh này có thể dùng để đặt glue cố định hoặc linh hoạt (khoảng cách):

* trước một bảng (tức là, bên trái cột 1);
* giữa một hoặc nhiều cột;
* sau bảng (tức là, bên phải cột cuối cùng).

Dưới đây là một ví dụ để nhắc lại:

![{{{alt}}}](/files/4afdb93b665d726c5004b33a2e5cadcfe2d9cb5c)

### Glue \tabskip ảnh hưởng thế nào đến độ rộng các cột gộp?

Sự hiện diện của `\tabskip` glue khác 0 giữa các cột cung cấp thêm khoảng trống mà các mục gộp có thể “hấp thụ” trước khi $$\mathrm\TeX$$ cần nghĩ đến việc tăng độ rộng của cột cuối cùng trong một phạm vi gộp.

Trong ví dụ tiếp theo, chúng ta sẽ dùng hai bảng để so sánh kết quả của việc gộp hai cột. Điểm khác nhau duy nhất giữa các bảng là việc dùng `\tabskip` glue.

* Ví dụ đầu tiên dùng bảng “tham chiếu” gốc của chúng ta, mà nếu bạn nhớ lại, đã đặt `\tabskip=0pt`.
* Ví dụ thứ hai dùng một phiên bản đã chỉnh sửa của [bảng tham chiếu](#reference-table) (đã chú thích ở trên) vốn có `\tabskip=10pt` trước và sau bảng nhưng, quan trọng hơn, nó đã đặt `\tabskip=20pt` giữa các cột.

Trong *đã chỉnh sửa* bảng tham chiếu, hai cột được gộp không ảnh hưởng đến độ rộng các cột (và độ rộng bảng) nhưng chúng lại ảnh hưởng đến độ rộng của cột 2 (và độ rộng bảng) trong bảng *gốc* [bảng tham chiếu](#reference-table).

### Bảng tham chiếu gốc: \tabskip=0pt

Ở đây, chúng ta cho thấy bảng [bảng tham chiếu](#reference-table) gốc cùng với một bảng thứ hai (được suy ra từ bảng gốc của chúng ta [bảng tham chiếu](#reference-table)) có một mục “**Kiểm tra một tiêu đề bảng dài hơn**” gộp các cột 1 và 2. Rõ ràng, cột 2 (của bảng thứ hai trong hình) và do đó toàn bộ bảng, đều bị ảnh hưởng bởi các cột gộp.

![{{{alt}}}](/files/6eed582a7697b263415ae3f1d2ce1d80c2c94d20)

### Bảng tham chiếu đã chỉnh sửa: \tabskip=20pt

Ở đây, chúng ta cho thấy bảng tham chiếu đã chỉnh sửa cùng với một bảng thứ hai (được suy ra từ bảng tham chiếu đã chỉnh sửa của chúng ta) cũng có một mục “**Kiểm tra một tiêu đề bảng dài hơn**” gộp các cột 1 và 2. Rõ ràng, trong bảng thứ hai ở hình, cả độ rộng của cột 2 lẫn của bảng đều không bị ảnh hưởng bởi các cột gộp. Trong trường hợp này, sự hiện diện của `\tabskip` glue (`20pt`) giữa các cột đã giúp “hấp thụ” khoảng trống cần thiết bởi văn bản trong mục gộp các cột 1 và 2:

![{{{alt}}}](/files/e8040172f4b15b5335fa85a3643c110ec65d85e4)

## Tinh túy của $$\mathrm\TeX$$’s algorithm

Hy vọng rằng, loạt ví dụ được đưa ra ở trên đã giúp hình thành một “cảm nhận” về điều mà $$\mathrm\TeX$$ làm để dung nạp các mục gộp và cách mà $$\mathrm\TeX$$ sẽ, nếu cần, điều chỉnh độ rộng của **cuối** cột trong mỗi phạm vi cột gộp. Bên cạnh độ rộng của các mục trong từng cột được gộp, sự hiện diện của `\tabskip` glue khác 0 là một yếu tố quan trọng mà $$\mathrm\TeX$$ xem xét khi quyết định liệu có cần điều chỉnh bất kỳ độ rộng cột nào hay không. Điểm mấu chốt cần nhớ là $$\mathrm\TeX$$mục tiêu của ’s là tính ra một độ rộng phù hợp cho **cột cuối** trong mỗi phạm vi các cột được gộp.

### Ví dụ bảng cuối cùng: các cột cuối trong một phạm vi được gộp

Trong ví dụ cuối cùng này, chúng ta lại sử dụng bảng tham chiếu đã chỉnh sửa của mình (với `\tabskip` các giá trị glue đã bàn ở trên) để suy ra một bảng khác chứa nhiều cột khác nhau được gộp bởi các rule—chúng tôi đã dùng các rule để làm cho các phần gộp dễ nhìn hơn.

Hai bảng đã được căn chỉnh cẩn thận để cho thấy rằng, ở bảng trên, không có cột nào trước cột 5 bị ảnh hưởng bởi các cột được gộp. Vùng xanh lá đậm bên trái sơ đồ cho thấy các cột 1 đến 4 của cả hai bảng vẫn thẳng hàng hoàn hảo. Bên phải là một vùng tô xanh lá nhạt cho thấy chỉ có các cột 5 và 6 bị ảnh hưởng bởi các mục được gộp.

Trong bảng phía trên, các phần gộp như sau:

* các cột 1 đến 5: được gộp bởi một $$400\text{pt}$$ rule;
* các cột 3 đến 5: được gộp bởi một $$200\text{pt}$$ rule;
* các cột 4 đến 6: được gộp bởi một $$250\text{pt}$$ rule.

![{{{alt}}}](/files/3e263278be1ba42e2ae28248598d847b1d9a066a)

Một lần nữa, lời giải thích là trong một chuỗi các cột được gộp, chỉ độ rộng của cột cuối cùng được điều chỉnh (nếu cần): các cột ở giữa không bị ảnh hưởng và ở đây điều đó có nghĩa là các cột 1 đến 4—mặc dù, dĩ nhiên, độ rộng của các cột 1 đến 4 (và glue ở giữa `\tabskip` được tính đến khi tính các độ rộng đã điều chỉnh của cột 5 và 6.

### Phần hướng dẫn từng bước về $$\mathrm\TeX$$’s algorithm

Chúng ta sẽ kết thúc bằng một *được đơn giản hóa* phần hướng dẫn từng bước về “$$\mathrm\TeX$$’s quá trình suy nghĩ” khi nó tính toán độ rộng của các cột trong các mục được gộp. Mô tả $$\mathrm\TeX$$các thuật toán của “ không phải lúc nào cũng đơn giản nên chúng ta sẽ áp dụng một chút “quyền tự do nghệ thuật đơn giản hóa” để cung cấp một cái nhìn tổng quan về những gì đang diễn ra. Những độc giả quan tâm đến toàn bộ các chi tiết rối rắm được tham khảo Mục 801 (trang 336) của cuốn sách in chứa $$\mathrm\TeX$$mã nguồn của [$$\mathrm\TeX\text{: The Program}$$](https://www.amazon.co.uk/Computers-Typesetting-Tex-Program-TEX/dp/0201134373).

Các bảng trong thực tế thường được tạo với nhiều lần sử dụng `\span` hàm nguyên thủy (ví dụ, bên trong $$\mathrm\LaTeX$$ các gói) để tạo nhiều trường hợp của các cột được gộp trong bảng. Để quản lý điều này, các cấu trúc dữ liệu (ở sâu bên trong $$\mathrm\TeX$$) lưu giữ thông tin (được gọi là *span nodes*) cho biết $$\mathrm\TeX$$ về các liên kết (spans) giữa các mục/cột của bảng. Rõ ràng, $$\mathrm\TeX$$ phải áp dụng các thuật toán của nó một cách có hệ thống và sẽ cần xử lý toàn bộ bảng để thực hiện các phép tính cuối cùng—toàn bộ để xác định tất cả độ rộng cột, tổng độ rộng của bảng và, nếu cần, lượng mà các glue linh hoạt dùng trong bảng phải giãn ra hoặc co lại. Thật ra không có gì ngạc nhiên khi $$\mathrm\TeX$$ không thể cho bạn biết độ rộng cuối cùng của bảng cho đến khi nó xử lý xong hoàn toàn lệnh `\halign{...}` —nó thật sự có rất nhiều việc phải làm!

Điểm khởi đầu cho việc tính độ rộng cột là cột 1 bởi vì, dĩ nhiên, không có gì có thể trải từ bên trái của (và *băng qua/vào*) cột 1. $$\mathrm\TeX$$ bắt đầu bằng việc xác định độ rộng của cột 1 bằng cách tìm mục nào có *độ rộng tự nhiên*. Hãy gọi độ rộng tối đa đó là $$w\_1$$và nếu có các mục trải từ cột 1 sang cột 2 thì hãy gọi độ rộng của mục đó là $$w\_{12}$$ (độ rộng từ 1 đến 2). Ngoài ra, chúng ta sẽ ký hiệu `\tabskip` glue giữa cột 1 và 2 là $$t\_{1}$$—lưu ý rằng chúng ta chỉ xét *độ rộng tự nhiên* của `\tabskip` glue đó và, hiện tại, bỏ qua mọi thành phần giãn hay co mà nó có thể có. Ngoài ra, hãy gọi độ rộng tự nhiên lớn nhất của tất cả các mục không gộp ở cột 2 là $$w\_2$$.

Điểm chính cần lưu ý là $$\mathrm\TeX$$ đang cố gắng tính độ rộng của cột 2 bằng cách chỉ xét những mục mà phần gộp *bắt đầu* ở cột 1 và *kết thúc* ở cột 2. Điểm xem xét chính đối với $$\mathrm\TeX$$ là phép kiểm tra $$\max(w\_{2}, w\_{12} - (w\_1+ t\_1))$$—có thể có nhiều mục trải qua cột 1 và 2: một số có thể hẹp (nhỏ $$w\_{12}$$), những mục khác rất rộng (lớn $$w\_{12}$$) nên $$\mathrm\TeX$$ đang tìm mục có tác động lớn nhất (do đó $$\max(\text{...})$$). Ở đây, giá trị của $$w\_{12} -(w\_1+ t\_1)$$ là lượng mà một mục trải qua cột 1 và 2 “tràn sang” từ cột 1 sang cột 2: lưu ý rằng $$\mathrm\TeX$$ đang sử dụng độ rộng của cột 1 **và** trang `\tabskip` glue ($$t\_{1}$$) giữa các cột 1 và 2. Một khi $$\mathrm\TeX$$ đã xác định xem bất kỳ phần gộp nào từ cột 1 đến 2 có thực sự ảnh hưởng đến độ rộng của cột 2 hay không, nó đặt độ rộng của cột 2 bằng giá trị lớn nhất mà nó đã xác định (theo phép kiểm tra đã mô tả). $$\mathrm\TeX$$ tiếp tục lần lượt đi qua tất cả các cột khác, thực hiện các phép kiểm tra tương tự.

Và cuối cùng, chỉ để đầy đủ, ở đây chúng tôi trích dẫn tinh túy của $$\mathrm\TeX$$’s algorithm để tính độ rộng cột (trích từ tài liệu mã nguồn của Knuth về $$\mathrm\TeX$$):

Hãy để $$w\_{ij}$$ là giá trị lớn nhất của các độ rộng tự nhiên của mọi mục trải qua các cột $$i$$ đến $$j$$, bao gồm cả hai đầu. Các độ rộng cột cuối cùng được xác định bởi công thức

$$\begin{equation\*} w\_j=\max\_{1\leq i\leq j}\biggl(w\_{ij}-\sum\_{i\leq k< j}(t\_k+w\_k)\biggr) \end{equation\*}$$

trong đó $$t\_k$$ là độ rộng tự nhiên của glue tabskip giữa các cột $$k$$ và $$k+1$$.

## Colophon: Sử dụng Overleaf để tạo các bảng dưới dạng đồ họa SVG

Tất cả $$\mathrm\TeX$$ các bảng được trình bày trong bài viết này đều là các tệp Đồ họa vector có thể mở rộng (SVG) được tạo trên nền tảng Overleaf. Các chú thích (mũi tên và các hộp màu xanh lá) được thêm vào bằng cách mở đồ họa SVG trong Inkscape—lưu ý, tuy nhiên, rằng văn bản của các chú thích được dàn chữ bằng $$\mathrm\TeX$$ như văn bản bổ sung đi kèm bảng: chỉ có các mũi tên và nền xanh lá được thêm trong Inkscape. Nếu bạn muốn biết điều này được thực hiện như thế nào, hãy đọc tiếp.

Các máy chủ của Overleaf sử dụng $$\mathrm\TeX \text{ Live}$$ bản phân phối này, ngoài $$\mathrm\TeX$$các bộ máy dàn chữ dựa trên -, cung cấp rất nhiều $$\mathrm\TeX$$công cụ và tiện ích phần mềm liên quan đến - rất hữu ích. Trong số đó có một công cụ tên là [`dvisvgm`](https://dvisvgm.de) mà, như tên gọi cho thấy, chuyển đổi $$\mathrm\TeX$$định dạng DVI truyền thống của **D**e**V**ice **I**ndependent) sang SVG. Trong số rất nhiều [tùy chọn dòng lệnh](https://dvisvgm.de/Manpage/) `dvisvgm` cung cấp một tùy chọn (`-n` hoặc `--no-fonts`) sẽ yêu cầu nó chuyển tất cả văn bản thành *đường dẫn* có nghĩa là văn bản trong các đồ họa SVG được vẽ bằng các đường thẳng và đường cong thay vì bằng phông chữ và glyph thực sự. Điều này có thể làm tăng kích thước tệp của đồ họa SVG thu được nhưng đảm bảo rằng các đồ họa SVG cực kỳ di động và gần như chắc chắn hoạt động tốt trên mọi thiết bị.

### Vậy... nó được thực hiện như thế nào?

Trong một [bài viết trước](https://www.overleaf.com/blog/510-using-luatex-to-run-tools-and-utilities-installed-on-overleafs-servers) tôi đã thảo luận về cách bạn có thể dùng $$\text{Lua}\mathrm\TeX$$ để chạy các công cụ và tiện ích phần mềm khác nhau được cài đặt trên các máy chủ của Overleaf—đây là một kỹ thuật cực kỳ dễ và tiện lợi. Kỹ thuật đó đã được dùng để tạo các đồ họa SVG của phần được dàn chữ $$\mathrm\TeX$$ các bảng, như sau. Từ bên trong tệp $$\mathrm\TeX$$ tài liệu chính, mã để dàn chữ từng bảng (được tạo bằng using using `\halign`) đã được ghi ra một `.tex` tệp. Điều này được thực hiện bằng cách đặt mã bảng trong một cặp lệnh mà tôi gọi là `\beginscoop` và `\endscoop`. Có lẽ còn nhiều cách khác để đạt được kết quả mong muốn nhưng dưới đây là các định nghĩa macro mà tôi đã dùng:

```latex
\def\cc{\catcode`\#=12\relax}
\long\def\scoop#1\endscoop{\global\fulltoks={#1}\egroup}
\def\beginscoop{\global\advance\numfigs by1\relax\bgroup\cc\scoop}
```

Bạn dùng chúng như sau:

```latex
\beginscoop
\halign{...}
\endscoop
```

Lưu ý rằng `\endscoop` token chỉ đơn thuần dùng để phân định tham số của `\scoop` macro: $$\mathrm\TeX$$ thực sự loại bỏ token `\endscoop` nên chúng ta thực ra không cần định nghĩa nó (ví dụ, bằng `\def\endscoop{...}`).

Tính năng $$\mathrm\TeX$$ mã chứa trong `\halign{...}` được lưu vào một `toks` thanh ghi có tên là `\fulltoks`. Một điểm khó tôi gặp phải (với $$\text{Lua}\mathrm\TeX$$) là cần ngăn chặn `#` các ký tự trong phần `\halign{...}` khai báo đầu bị “nhân đôi” thành `##` khi được ghi ra một `.tex` tệp. Để tránh điều này tôi phải tạm thời đặt `\catcode`của `#` các ký tự thành 12 trước khi lưu $$\mathrm\TeX$$ mã (các token) trong `\fulltoks` thanh ghi token.

Bước tiếp theo là ghi các token có trong `\fulltoks` thành một $$\mathrm\TeX$$ tệp—vì tôi đang dùng $$\text{Lua}\mathrm\TeX$$ điều này tỏ ra rất *cực kỳ* dễ nhờ $$\text{Lua}\mathrm\TeX$$’s Lua API tuyệt vời. Tóm lại, tôi đã viết một macro có tên là `\writefile{...}` mà tham số của nó là tên của một thanh ghi token mà bạn muốn ghi các token của nó ra một tệp (ví dụ, `\writefile{fulltoks}`). Bên trong macro `\writefile{...}` tôi đã dùng Lua API để lấy một biểu diễn văn bản của `\fulltoks` thanh ghi token:

```latex
\def\writefile#1{%
\directlua{
...
...
 local p=tex.toks["#1"]
...
...
}}
```

Dưới đây là một ảnh chụp màn hình cho thấy thêm một chút về `\writefile{...}` lệnh:

[![{{{alt}}}](/files/656677659cb21ea55a202ffbecea2ed1af83d8db)](https://www.filepicker.io/api/file/ngeDmgRStGWvG044RE1A)

Ngôn ngữ Lua và Lua API do $$\text{Lua}\mathrm\TeX$$ có thể thường xuyên đơn giản hóa $$\mathrm\TeX$$ các tác vụ lập trình và chính nhờ những tính năng hữu ích và mạnh mẽ này mà tôi đã sử dụng $$\text{Lua}\mathrm\TeX$$ từ khoảng năm 2009—và vẫn là một người hâm mộ lớn của bộ máy thật sự tuyệt vời này $$\mathrm\TeX$$ bộ máy. Được rồi, quảng cáo $$\text{Lua}\mathrm\TeX$$ giờ kết thúc.

Sau khi dễ dàng lấy được $$\mathrm\TeX$$ mã được lưu trong `\fulltoks` nó được ghi ra một tệp cùng với một số mã bổ sung để biến nó thành một $$\mathrm\LaTeX$$ tệp được định dạng đúng. Các bước tiếp theo là:

1. Xử lý tệp `.tex` chứa bảng của chúng ta bằng $$\text{pdf}\mathrm\LaTeX$$ (ở chế độ DVI) để nó dàn chữ bảng và tạo ra một `.dvi` tệp để `dvisvgm` xử lý. Đúng vậy, bạn có thể dùng $$\text{Lua}\mathrm\TeX$$ để chạy $$\text{pdf}\mathrm\LaTeX$$—một lần nữa tôi đã dùng phương pháp được thảo luận trong một [bài viết trước](/latex/vi/bai-viet-chuyen-sau/52-using-luatex-to-run-tools-and-utilities-installed-on-overleaf-s-servers.md).
2. Và cuối cùng, chạy `dvisvgm` để xử lý `.dvi` tệp để tạo một đồ họa SVG của bảng đã dàn chữ $$\mathrm\TeX$$ bảng.
3. Để lấy các đồ họa SVG thực sự, bạn có thể tải xuống một tệp ZIP từ Overleaf—hãy đảm bảo chọn **Tệp Đầu vào và Đầu ra** tùy chọn.


---

# 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/47-tex-tables-how-tex-calculates-spanned-column-widths.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.
