amsmath 自 1990 年代以来几乎没有变化。稳定是优点,代价却是积攒了一堆「想改却改不了」的小别扭——:= 的样子、绝对值竖线不肯随内容长高、求和号下写一长串条件就把整个公式撑宽。mathtools 就是 2002 年为安放这些修补而生的 LaTeX 宏包;它不取代 amsmath,而是先加载 amsmath,再在其上叠加。本页依次讲解 \DeclarePairedDelimiter、\coloneqq、\mathclap、\prescript、matrix*、dcases 和 showonlyrefs,并附上实测尺寸。它们每一个都是 amsmath 独自迈不出去的那一步。
amsmath 和 mathtools 该 \usepackage 哪一个
只写 mathtools 就够了。 mathtools 内部会加载 amsmath,因此不必另写 \usepackage{amsmath}。而且给 amsmath 的选项会原样透传——\usepackage[fleqn,tbtags]{mathtools} 等同于先写 \usepackage[fleqn,tbtags]{amsmath} 再写 \usepackage{mathtools}。用不着两个都写,也用不着操心顺序。
% one line is enough: mathtools loads amsmath itself
\usepackage{mathtools}
% options aimed at amsmath are passed straight through
\usepackage[fleqn,tbtags]{mathtools}这种「叠在 amsmath 之上」的设计,经历了三代维护者仍未改变。mathtools.sty 的版权声明写着:2002—2011 年为 Morten Høgholm,2012—2019 年为 Lars Madsen,2020 年起是 Lars Madsen 与 LaTeX3 项目。也就是说,mathtools 已从一个人的顺手工具,升格为由 LaTeX 本体开发团队照料的准标准。这一点看得出来:如今含公式的文档,导言区第一行写的往往不是 amsmath 而是 mathtools。mathtools 还依赖辅助宏包 mhsetup,但 TeX Live 等发行版都已随附,无需操心。
让绝对值和范数自动匹配大小 — \DeclarePairedDelimiter
在导言区写一行 \DeclarePairedDelimiter\abs{\lvert}{\rvert},就得到一个可在三种大小之间切换的自定义 \abs。大家最先想到的自制写法 \newcommand{\abs}[1]{\lvert#1\rvert} 确实把横向间距做对了,但一旦塞进 \abs{\frac{a}{b}} 这种高个子内容,竖线就不肯长高,孤零零地立在超出它的分数旁边。\DeclarePairedDelimiter 接手的正是这一步。把命令名用花括号包起来写成 \DeclarePairedDelimiter{\abs}{\lvert}{\rvert} 同样可以编译。
% in the preamble; braces around the command name are optional
\DeclarePairedDelimiter\abs{\lvert}{\rvert}
\DeclarePairedDelimiter\norm{\lVert}{\rVert}
% three ways to call it
\[
\abs{x} = \abs{-x}
\qquad
\abs*{\frac{a}{b}} = \frac{\abs{a}}{\abs{b}}
\qquad
\norm[\big]{v}
\]差别不靠感觉,可以量出来。在 TeX Live 2024 下,\sbox0{$\abs{\frac ab}$} 的高度是 7.5pt,带星的 \abs*{\frac ab} 是 8.50006pt。星号会把内容包进 \left … \right,竖线于是随分数的高度长高。第三种形式传入 [\big]、[\Big]、[\bigg]、[\Bigg],是手动选档;本例中 [\big] 同样量得 8.50006pt,恰好落在自动挡选中的那一档。记成 星号=拉伸,方括号=钉在这一档,就不会弄混。若记号需要在定界符内部再加分隔,例如 \Set{x}{x>0},则用 \DeclarePairedDelimiterX。
| 写法 | 大小 | 适用场合 |
|---|---|---|
\abs{x} | 固定(实测 7.5pt) | 内容是矮的变量或数字时 |
\abs*{x} | 自动(实测 8.50006pt) | 分数、根号、求和等高个子内容 |
\abs[\big]{x} | 手动指定档位 | 想让并排的式子高度一致时 |
\DeclarePairedDelimiterX | 同上,可接受多个参数 | 带内部分隔的记号,如 \Set{x}{x>0} |
:= 为什么看着不对,以及 \coloneqq 到底改了什么
要修的有两处:冒号的高度和冒号与等号之间的空隙。常见的说法是「: 不是关系符,所以间距不对」——这是错的。让 LaTeX 显示 : 的 math code,得到 12346,十六进制是 "303A,开头的 3 表示 第 3 类,即关系符。因此写成 $a\mathrel{:=}b$ 根本不会有任何变化。实测也印证:$a:=b$ 与 $a\mathrel{:=}b$ 都是 25.68855pt,完全相同。
那么 \coloneqq 做了什么?打开定义可见 \vcentcolon\mathrel{\mkern-1.2mu}=。第一,它不用普通的 :,而用 \vcentcolon,即居中于数学轴的冒号(实测 $:$ 高 4.30554pt,$\vcentcolon$ 高 4.65277pt,抬高了 0.34723pt)。第二,用 -1.2mu 的负间距把冒号拉向等号;因为两个关系符相邻时,TeX 也会在它们之间插入关系符间距,于是 : 与 = 看着像两个分开的符号。结果:$a\coloneqq b$ 实测 25.02191pt,比裸写的 := 窄 0.66664pt,恰好等于 10pt 字号下的 1.2mu。
\[
f(x) \coloneqq x^2 + 1, \qquad y \eqqcolon g(x), \qquad
A \Coloneqq B
\]有一个坑。mathtools 改变了 \coloneq 的含义。如今默认情况下 \coloneq 输出与 \coloneqq 相同的「:=」;而在旧定义中它输出的是冒号加短横「:−」。\eqcolon 也一样,现在与 \eqqcolon 同为「=:」。需要旧字形的文档可用 \usepackage[legacycolonsymbols]{mathtools}。反过来说,\coloneqq 与 \eqqcolon 从未被废弃,在两种设置下输出相同——拿不准时就写带两个 q 的那个,最稳妥。同族还有 \dblcolon(::)、\Coloneqq(::=)、\Eqqcolon、\colonapprox、\colonsim,以及新增的 \approxcolon 和 \simcolon。
求和号下的长条件把式子撑宽 — \mathclap 与 \smashoperator
把条件放进零宽度的盒子,问题就消失了。\sum_{1 \le i \le j \le n} a_{ij} 整体量得 48.21771pt;把条件包成 \sum_{\mathclap{1 \le i \le j \le n}} a_{ij} 后是 28.43983pt,窄了 19.78pt。条件的字符并没有消失,依旧清清楚楚地待在求和号下方,只是它们的宽度不再计入式子的总宽。\mathclap 让溢出向两侧均分,\mathllap 向左,\mathrlap 向右。正文模式下的 \clap 也是同一思路。
% the wide condition stretches the whole formula
\[ \sum_{1 \le i \le j \le n} a_{ij} \]
% zero-width box: same look, width of the operator only
\[ \sum_{\mathclap{1 \le i \le j \le n}} a_{ij} \]
% same result, but this one can flatten a single side
\[ \smashoperator{\sum_{1 \le i \le j \le n}} a_{ij} \]
\[ \smashoperator[r]{\sum_{1 \le i \le j \le n}} a_{ij} \]mathtools 还提供了 \smashoperator。实测 \smashoperator{\sum_{1 \le i \le j \le n}} 为 28.43983pt,与 \mathclap 的结果分毫不差。区别在于灵活性:\smashoperator 把运算符连同上下标一起接收,并可用 [l]、[r]、[lr](默认)选择压平哪一侧。当求和号位于左边界、不能让它向左溢出时,[r] 就派上用场——实测 38.32878pt,介于双侧压平与原始宽度之间。\mathclap 用来瞄准式子内部的某一点,\smashoperator 则用来照料行首那个运算符。
把角标放到符号左边 — \prescript 与 \adjustlimits
\prescript{上}{下}{主体} 三个参数,可把角标挂到符号的左上与左下。同位素记号是最清楚的例子:\prescript{238}{92}{\mathbf{U}} 排出铀-238,质量数 238 在左上,原子序数 92 在左下。也可以手写 {}^{238}_{92}\mathbf{U},但那是把角标挂在空组上,位置和大小都不可靠。若不需要左下角标,把第二个参数留成空的 {}。角标的字体可通过 \mathtoolsset 的 prescript-sub-format、prescript-sup-format 更换。
\[
\prescript{238}{92}{\mathbf{U}}, \qquad
\prescript{n}{}{C}_{k}
\]
% limits of two nested operators lined up
\[ \adjustlimits \sum_{i} \sum_{j} a_{ij} \]这一节还该放一个工具,不起眼却管用。\adjustlimits 会对齐两个嵌套运算符的下限。像 \sum_i \sum_j 这样大小不同的角标接连出现时,第二个下限有时看着略微上浮,\adjustlimits 就吸收掉这一段差。实测 \sum_i\sum_j 为 30.55557pt,\adjustlimits\sum_i\sum_j 为 32.2222pt——相差 1.66663pt,恰是 3mu 的调整量。遇到 \lim_{n\to\infty}\sup_{x} 这类宽度不同的运算符相接时同样有效。
让矩阵的列对齐 — pmatrix* 与 [r] [l] [c]
amsmath 的 pmatrix 一律把列居中。 一旦出现负数,这就成了麻烦:-1 与 2 上下相邻时,数字看着会错开一个负号的宽度。mathtools 为每个环境提供了带星版本——matrix*、pmatrix*、bmatrix*、Bmatrix*、vmatrix*、Vmatrix*、smallmatrix*——并以可选参数接受与 array 相同的列格式。写 \begin{pmatrix*}[r] 即右对齐,负号会利落地向左伸出。默认值是 c,所以只加星号不会改变外观。小矩阵的默认对齐可用 \mathtoolsset 的 smallmatrix-align 设定。
\[
\begin{pmatrix*}[r]
-1 & 3 \\
2 & -4
\end{pmatrix*}
\qquad
\begin{pmatrix}
-1 & 3 \\
2 & -4
\end{pmatrix}
\]分情况与过长的一行 — dcases、rcases、multlined
amsmath 的 cases 以行内样式(text style)排版内容,因此放进分数会被压扁,\frac{1}{2} 会缩小。mathtools 的 dcases 写法照旧,但把主体按陈列尺寸排——d 取自 displaystyle。若希望花括号在右侧,即由多个条件导出一个结论,则用 rcases,并在 \end{rcases} 之后接 \Rightarrow 之类。还有二者合并的 drcases,以及把最后一列按正文排版的 dcases*、rcases*。需要自定义的分情况环境时,可用 \newcases 造一个。
\[
f(x) =
\begin{dcases}
\frac{1}{2} & x > 0 \\
0 & x \le 0
\end{dcases}
\qquad
\begin{rcases}
a \\ b
\end{rcases} \Rightarrow c
\]
% [t] aligns on the first row; the second optional argument fixes the width
\[
\begin{multlined}[t][0.6\textwidth]
a + b + c + d \\ {} + e + f
\end{multlined}
\]multlined 是把 amsmath 的 multline 环境收进一个盒子的版本。multline 会独占整个陈列行,因此不能用在 align 内部或 = 的右边。multlined 是环境,可以嵌进式子的任何位置,只在内部折行处理过长的右端。它有两个可选参数:第一个是与外部的纵向对齐位置(t/b,默认居中),第二个是宽度。若不指定宽度,它只取所需的宽度。想在两个对齐行之间插入箭头可用 \ArrowBetweenLines;想放宽整个对齐环境的行距,则用 spreadlines 环境。
只给被引用的公式编号 — showonlyrefs
写下 \mathtoolsset{showonlyrefs},只有正文引用过的公式才会带编号,其余的编号会消失。草稿阶段公式编号总在增增减减,给没人引用的公式编号,只会让审稿人去找「(7) 用在哪里」。在 TeX Live 2024 下排一个含两式的 align,仅用 \eqref 引用其中一个,结果被引用的式子得到 (1),另一个完全没有编号。只有一个前提:引用要用 \eqref 而非 \ref。 如果连 \tag{} 手动指定的编号也不想丢失,再加上 showmanualtags。
\mathtoolsset{
showonlyrefs, % number only what \eqref points at
showmanualtags, % but keep \tag{} numbers visible
centercolon % a bare : is set at axis height
}这些命令究竟来自 amsmath 还是 mathtools
只加载 mathtools 时界线会变模糊,但在修改他人文档、或遇到只允许 amsmath 的投稿规定时,这条界线就很要紧。打开 .sty 即可分辨。可伸缩箭头 \xrightarrow、\xleftarrow 属于 amsmath(定义在 amsmath.sty 中),mathtools 补的是其余同族——双线的 \xRightarrow、\xLeftarrow、\xLeftrightarrow,双向的 \xleftrightarrow,带钩的 \xhookrightarrow、\xhookleftarrow,映射的 \xmapsto,更长的 \xlongrightarrow,以及 harpoon。它们都用同样的 [下]{上} 写法。\intertext 属 amsmath,收紧版 \shortintertext 属 mathtools。\overset、\underset、\substack 属 amsmath;直角的 \overbracket、\underbracket 属 mathtools。
| 命令/环境 | 出处 | 作用 |
|---|---|---|
\xrightarrow | amsmath | 随标签宽度伸长的箭头;[下]{上} |
\xleftrightarrow | mathtools | 扩充可伸缩箭头家族:\xRightarrow、\xmapsto 等 |
\overbracket | mathtools | \overbrace・\underbrace 的直角版;第一个可选参数是线宽,第二个是高度(默认 0.7ex) |
\intertext | amsmath | 在行间插入文字而不破坏对齐 |
\shortintertext | mathtools | 同上,但收紧上下留白 |
\substack | amsmath | 把角标堆成多行,与用 \mathclap 横向压缩相对 |
\MoveEqLeft | mathtools | 把 align 首行左推,使后续各行与之对齐 |
\splitfrac | mathtools | 当分子或分母过长时,仅在该处折成两行 |