TeX 本身并没有 \begin 这个命令。Knuth 的 plain TeX 只定义了 \beginsection,而 \begin{...} … \end{...} 这种写法是 LaTeX 后来加上的。那么它是为什么被加上的?答案是一个承诺——一旦越过 \end,你在内部改动过的一切都会恢复原状。这个被称为 环境(environment) 的机制,让列表、陈列公式、图表、引用,乃至文档正文本身,全都采用同一种形态。本页说明 LaTeX 的环境保证了什么、环境如何嵌套,以及当两端名称不一致时出现的 \begin{...} ended by \end{...} 该怎么读。
\begin 与 \end 保证了什么
环境保证的是「分组」。 \begin{name} 甚至在弄清这个环境到底要做什么之前,就先开启一个 TeX 分组,而 \end{name} 再把它关闭。因此无论你在内部改字体还是改边距,越过 \end 之后都不会留下任何痕迹。center 环境把内容居中,itemize 环境把内容变成项目符号列表,quote 环境加宽左右边距,而每一种效果都在各自的范围内收尾。反过来说,环境提供的不是装饰,而是善后的保证,这正是「优先使用环境」成为定式的原因。
\begin{name} 与 \end{name} 的名称必须完全一致:不能用 \begin{center} 打开却用 \end{flushleft} 关闭。在幕后,LaTeX 做着一件不起眼的工作——记住当前打开的环境名称。正因如此,它的错误消息才能同时给出两个名称以及开启处的行号。文档整体也不例外:\begin{document} … \end{document} 是货真价实的环境,而「正文写在这里」这条最小规则,不过是「写在最外层环境之内」这一寻常事实罢了。
\begin{center}
This paragraph is centred; the line after \end is not.
\end{center}
\begin{quote}
{\itshape Everything inside is a group,}
so this italic never escapes the environment.
\end{quote}嵌套:按打开顺序的相反顺序关闭
环境可以嵌套,但关闭顺序必须与打开顺序完全相反:在 \begin{a} 内部打开 \begin{b},就要先写 \end{b},再写 \end{a}。范围不能交叉,也就是不能写成 \begin{a}\begin{b}...\end{a}\end{b}。原因正是上一节的那个保证:既然环境就是分组,分组只能像盒子那样彼此包含,不存在只重叠一半的形状。引用中的列表、图中的表格、定理中的公式——实际会遇到的组合,全都能顺理成章地写成这种嵌套。
\begin{quote}
A bulleted list inside a quotation:
\begin{itemize}
\item first
\item second
\end{itemize}
\end{quote}这里值得养成的习惯只有一个:打开环境后,先把关闭行写出来,再去写中间的内容。 然后把内部缩进两格或四格,figure 中的 tabular、quote 中的 itemize 这样的配对关系,就会以源码形状的方式呈现出来。嵌套越深,日后替换内容时把某个 \end 弄错的事故就越容易发生,而正如下一节所述,这类事故并不会在你出错的地方显现。
最先遇到的环境:按用途分类
标准 LaTeX 提供的环境,分成正文对齐、列表、数学、浮动体、表格这五类来看,整体就一目了然。各自的详细用法留给专门页面,但形态完全相同——用 \begin 打开、用 \end 关闭,内部是一个分组。遇到新环境时,先问一句「它划出的是什么范围」,通常就八九不离十了。
| 类别 | 环境 | 作用 |
|---|---|---|
text | center, flushleft, flushright, quote, quotation, verbatim | 居中、左对齐、右对齐;引用;按输入原样输出 |
lists | itemize, enumerate, description | 项目符号、编号、带标签的列表 |
math | equation, align, gather | 陈列公式、多行对齐、编号 |
floats | figure, table | 带标题和编号,把位置交给排版程序决定 |
tabular | tabular, array | 行与列的格子本身,与 figure/table 职责不同 |
这张表里有三组特别容易混淆。quote 与 quotation:两者都会把引用内容从左右缩进,但 quotation 还会额外缩进每段首行,更适合跨越多段的长引用。figure/table 与 tabular:这是完全不同的东西——前一对是「会浮动、带标题和编号的容器」,后者是「格子本身」。若表格需要编号和交叉引用,就把 tabular 放进 table 里。而 verbatim 会用等宽字体按输入原样输出,连 \ 和 { 都不解释为命令,是展示程序源码的常用手段。
有些环境带参数,写在紧跟 \begin 之后的 {...} 或 [...] 中。例如 tabular 必须指定列格式:\begin{tabular}{cc} 中的 {cc} 给出各列对齐方式(c 居中、l 左、r 右),内部用 & 分隔列,每行以 \\ 结束。同样,figure 以选项形式接收位置意向,如 \begin{figure}[htbp]。参数总是紧接在环境名之后,\end 那一侧永远什么都不写。
\begin{figure}[htbp]
\centering
\begin{tabular}{ll}
input & output \\
source & PDF \\
\end{tabular}
\caption{A tabular grid inside a floating table}
\label{tab:demo}
\end{figure}align 与 align* 的区别:星号是名称的一部分
名称末尾带 * 的形式,就是「不编号的版本」。 align 会给每一行加式号,而 \begin{align*} … \end{align*} 一个都不加。equation* 同理,是没有编号的陈列公式。有趣的是,这个 * 既不是选项也不是参数,而是名称本身的一部分。\begin 会把传入的字符串直接拼成命令名,于是真的诞生了一个名叫 align*(含星号)的命令。正如上一页所述,控制词的名称会在第一个非字母处结束,所以这个命令原理上无法用 \align* 调用,只能经由 \begin{align*} 这条路径触及。
\begin{align} % every line gets an equation number
a &= b + c \\
d &= e + f
\end{align}
\begin{align*} % same layout, no numbers at all
a &= b + c \\
d &= e + f
\end{align*}出现 \begin{...} ended by \end{...} 时
这条消息说的不是「你关错了」,而是「当时打开的是这个」。 名称不一致时,LaTeX 会以类似 ! LaTeX Error: \begin{center} on input line 5 ended by \end{flushleft}. 的形式停下,同时给出被打开的环境名称及它的行号。该读的是前半段而不是后半段的 \end——错误被报告的行几乎总是 \end 那一侧,而真正需要修改的地方是前半段所指的行。漏写 \end 时消息形式相同,只是对方变成了 \end{document}:出现 ! LaTeX Error: \begin{itemize} on input line 2 ended by \end{document}. 就表示第 2 行打开的 itemize 自始至终没有被关闭。
名称打错时情况略有不同。写下未定义的名称,例如 \begin{itemzie},先会得到 ! LaTeX Error: Environment itemzie undefined.,接着是 ! LaTeX Error: \begin{document} ended by \end{itemzie}.。之所以出现两条错误,是因为 \begin 在检查该环境是否存在之前就已经开启了分组。分组就这样一直开着,于是对应的 \end 便找不到配对对象。由此可得一条实用准则:从上往下一次只修一个错误再重新编译,第一个之下的全部忽略。 在环境相关的问题上,第一个错误往往正是第二个错误的成因。
还有一条能把两者区分开的线索。忘记关闭花括号 { 时,输出的是 TeX 自身而非 LaTeX 的消息。 在日志接近末尾处,只会看到 (\end occurred inside a group at level 1),且没有行号。因为留下的是一个没有名字的普通分组,TeX 没有材料回答「在哪里」。反过来看,这就成了诊断依据:带行号的消息意味着环境不匹配,只有这一条不带行号的消息则意味着花括号。需要缩小范围时,把可疑的环境切到单独文件里单独编译,仍然是最快的路子。
按含义而非外观选择环境
环境不是视觉上的盒子,而是稿件部件的名字。 因为是引用所以选 quote,因为是项目列表所以选 itemize,因为是格子所以选 tabular,因为需要编号和交叉引用所以选 figure/table——照这样选,日后标题、编号、引用、图表目录都会顺理成章地接上。反之,若一直按外观来选——「想居中所以用 center」「想要边距所以用 quote」——半年后你将无法判断某个盒子当初是干什么的,也无法一次性更改整体版式。如果只是想改外观,那么老老实实使用声明或间距命令,比使用环境更诚实。
- 内容是引用 — 使用
quote/quotation,并在正文或脚注中注明来源。 - 内容是逻辑列表 — 使用
itemize/enumerate/description;不要用\\伪造项目符号。 - 图或表需要编号和引用 — 在
figure/table中放置\caption和\label,格子交给tabular。 - 同一组合反复出现 — 不要复制局部格式命令,用
\newenvironment起个名字,把含义固定下来。
正如最后一条所暗示的,环境不仅可以使用,还可以自己定义。\newenvironment 用来给出「开始时执行的代码」和「结束时执行的代码」;若要改写已有名称,则使用 \renewenvironment。参数如何传递、为什么 \end 一侧不能使用参数、以及基于 NewDocumentEnvironment 的现代写法等细节,都整理在自定义环境的专门页面。此处只需记住一点:你自己定义的环境,也会原封不动地继承本节所说的分组保证。