enumerate

LaTeX 文档末尾参考文献表里那一列 [1] [2] [3],编号其实出自 enumerate第四层计数器article.cls 中的 thebibliography 并没有自建计数器,而是直接写 \usecounter{enumiv},借用了编号列表最深一层的号码。列表机制就是这样深深嵌进文档骨架里的。本页按顺序讲 enumerate:保存号码的计数器 enumienumiv、把号码变成文字的 \theenumi 一族、如何用 \label\ref 指向某一条,以及如何让被打断的列表接着编号。

号码存在计数器里,而不在环境里:enumi 到 enumiv

enumerate 自己并不持有号码。latex.ltxenumienumiienumiiienumiv 定义成四个普通的 LaTeX 计数器,环境只是按当前深度挑其中一个来用。工作分成三层:\begin{enumerate} 调用 \usecounter 把计数器归零;\item 在排出条目正文之前用 \refstepcounter 加一;\theenumi 一族再把这个值转成文字。记住这个顺序,后面那个「想让列表从 5 开始却要写 4」的怪事就一下子说得通了。

层级计数器默认格式行首标签输出
enumi1\theenumi = \arabic{enumi}\labelenumi = \theenumi.1.
enumii2\theenumii = \alph{enumii}\labelenumii = (\theenumii)(a)
enumiii3\theenumiii = \roman{enumiii}\labelenumiii = \theenumiii.i.
enumiv4\theenumiv = \Alph{enumiv}\labelenumiv = \theenumiv.A.

把表中第三列与第四列混为一谈,后面一定会吃亏。\theenumi 决定号码本身怎么写,\labelenumi 决定放在行首的那层修饰。看第二层就很清楚:\theenumii 只说了 a,括号是 \labelenumii 加上去的。想换号码的样式就改 \theenumi,想换它前后的括号或句点就改 \labelenumi。把计数变成字符的命令共五个——\arabic(1, 2, 3)、\alph(a, b, c)、\Alph(A, B, C)、\roman(i, ii, iii)、\Roman(I, II, III)——对任何计数器都适用。

latex
% style of the number itself
\renewcommand{\theenumi}{\Roman{enumi}}
% punctuation around it: I) II) instead of I. II.
\renewcommand{\labelenumi}{\theenumi)}

用 \label 与 \ref 指向某一条——但引用出来的号码和行首不一样

\label{键} 放在某个 \item 正文的开头,文档任何位置的 \ref{键} 就能印出该条的号码。\item 用来推进号码的 \refstepcounter,在加一的同时还会记下「当前可被引用的对象就是这一条」,这正是 \label 与紧邻其前的 \item 绑定起来的原因。步骤重新排序后引用能自动跟上,靠的就是这个机制。数值经由 .aux 文件传递,所以新加 \label 的那一次编译号码还没定下来,必须编译两遍。若加载了 hyperref,同一个引用还会变成可点击的链接。若想要的不是条目的号码而是它所在的页码,用同一个键写 \pageref{键} 即可。

latex
\begin{enumerate}
  \item Bring the sample to room temperature
  \item \label{step:weigh} Record the weight
  \item Add solvent and stir
\end{enumerate}
Use the value from step~\ref{step:weigh} in the yield calculation.

这里有一个不知道就一定会愣住的行为:引用一个嵌套条目时,返回的字符串和行首看到的号码并不一样。 第二层的条目排出来是「(a)」,\ref 却返回 2a;第三层看到的是「i.」,引用出来却是 2(a)i。这不是缺陷,而是一个叫 \p@enumii 的「前缀」命令在起作用。article.cls\p@enumii 定义为 \theenumi,引用返回的字符串按 前缀 + \theenum… 拼成。负责行首标签的 \labelenumii 完全不参与引用,所以它加的那对圆括号在引用里也不会出现。

层级行首看到的号码\ref 返回的字符串前缀的定义
enumi2.2无前缀
enumii(a)2a\p@enumii = \theenumi
enumiiii.2(a)i\p@enumiii = \theenumi(\theenumii)
enumivA.2(a)iA\p@enumiv = \p@enumiii\theenumiii

这个设计本身是合理的:光写一个「a」,读者无从知道指的是哪一条父项下的 (a)。不过「2a」这种写法常常与文档的体例不合。若想去掉父级、只返回 a,就定义 \renewcommand{\p@enumii}{};若想写成「2.1」,就用 \renewcommand{\p@enumii}{\theenumi.}。这里千万不要去动 \theenumii——改了它,行首标签也会变成「2.1」,把列表的外观弄坏。只想修引用时,该动的是 \p@enum…

让被打断的列表接着编号:为什么想从 5 开始却要写 4

如果流程中间插进一段说明或一张图而不得不把列表关掉,下一个 enumerate 自然会从 1 重新开始——因为 \begin{enumerate} 调用的 \usecounter 每次进入环境都会把计数器归零。想接着编号,就在打开列表紧接着的位置\setcounter 重新写入数值。这时前面那三层顺序就派上了用场:\item 会在排出正文之前给计数器加一,所以想从 5 开始,就要设成 4。「减一」不是什么口诀,而是 \refstepcounter 先跑一步这一事实的直接结果。

latex
\newcounter{savedstep}

\begin{enumerate}
  \item first
  \item second
\end{enumerate}
\setcounter{savedstep}{\value{enumi}}

An interrupting paragraph.

\begin{enumerate}
  \setcounter{enumi}{\value{savedstep}}
  \item this one is numbered 3
\end{enumerate}

这种写法之所以可靠,是因为 enumi\end{enumerate} 之后仍保留着最后的数值。列表一关就把它存进自建的计数器,中间无论插进多少段落和插图,编号都能接上。要接续更深的层级,enumiienumiiienumiv 同理。不过,与其每次都写这套样板,不如加载 enumitem\begin{enumerate}[resume] 从上一个列表停下的地方接着走,\begin{enumerate}[start=7] 直接从 7 开始(用 start= 就不必再「减一」)。若要写较长的操作手册,一开始就引入 enumitem 是值得的。

什么时候该编号,什么时候不该

选择 enumerate 的理由,不是「有号码看起来整齐」。号码是与读者之间的契约,它宣告顺序、优先级或引用这三者之中至少有一项在此确有意义。给本无顺序的条目编上号,读者就会自行读出时间先后或轻重缓急,从而生出作者从未打算表达的主张。反过来,在真正讲究次序的操作手册里,号码正是让读者能说出「回到第 3 步」的工具。而这种引用应交给 \label\ref,不要在正文里直接敲一个「3」:手打的号码,只要插进一条,就会全部变成谎言。

  • 流程、时间顺序、优先级用 enumerate 读者能说出「回到第 3 条」,是编号唯一的理由。
  • 无序的条件与特征用 itemize 给它们编号,等于主张了你并不想表达的高下或先后。
  • 要引用的条目一定要加 \label 在正文里直接敲号码,只要多加一条就全错位。
  • 核对 \ref 对嵌套条目返回什么。 行首写着「(a)」,引用回来却是 2a
  • 被打断的流程要接着编号。 纯 LaTeX 用 \setcounterenumitemresume