多数人记得 LaTeX 的 itemize 在各层给出的四个行首标签(标记)是"•、-、、·"。可一旦翻开 article.cls,后三个竟无一与设想相符。第 2 层不是两个连字符,而是 粗体的 en 破折号(\labelitemfont \bfseries \textendash);第 3 层不是寻常的星号,而是 上下居中的星号* \textasteriskcentered;第 4 层则是 \textperiodcentered。自定义标签,须从弄清这些宏究竟装着什么开始。本页依次考察 itemize 的四个标记、enumerate 中 \theenumi 与 \labelenumi 的分工、从 \arabic 到 \fnsymbol 的计数器表示命令及各自的极限,以及嵌套的真正上限——也就是 ! LaTeX Error: Too deeply nested. 究竟何时才会出现。
itemize 的四个默认标记究竟是什么
itemize 的行首符号分装在四个宏里,每层一个:\labelitemi、\labelitemii、\labelitemiii、\labelitemiv(词尾的 i/ii/iii/iv 是罗马数字,代表第 1 至第 4 层)。它们在标准文档类中的定义见下表,四者开头都带 \labelitemfont——那是 article.cls 中定义为 \normalfont 的一个小钩子,它保证即便周围文字是斜体或粗体,标记仍以正文字体排出。唯有第 2 层在 \labelitemfont 之后还接了 \bfseries,故其 en 破折号排成粗体。它看上去比邻居更浓,原因就在这里。
| 宏 | 标准文档类中的定义 | 实际印出的字 |
|---|---|---|
\labelitemi | \labelitemfont \textbullet | • 实心圆点 |
\labelitemii | \labelitemfont \bfseries \textendash | – 粗体 en 破折号,并非两个连字符 |
\labelitemiii | \labelitemfont \textasteriskcentered | ∗ 上下居中的星号,与 * 并非同一字形 |
\labelitemiv | \labelitemfont \textperiodcentered | · 间隔号 |
要更换 整层 的符号,就用 \renewcommand 重新定义对应的宏,置于导言区或所需作用范围之前。有一点须留意:这些宏 不会 自行进入数学模式。若不自己写上美元符号,如 $\diamond$,则 \diamond 在正文中无法使用,会报错。常用的替代符号有 $\diamond$(◇)、$\ast$(∗)、$\cdot$(·)、$\triangleright$(▷);若载入 pifont 宏包,还可使用 \ding{"6C} 之类的装饰符号。不过切莫过分讲究:符号越花哨,层级越难分辨,因此通常只改第 1 层最为稳妥。
% the whole of level 1 becomes a diamond -- note the explicit math shift
\renewcommand{\labelitemi}{$\diamond$}
% or, with enumitem, the same thing scoped to one list
\begin{itemize}[label=$\diamond$]
\item this list only
\end{itemize}只替换单个条目的标签:\item[...]
若只想改 某一个条目 的标记,就在 \item 之后用方括号给出标签:\item[$\star$] 使该条目行首成为星号,\item[--] 则为破折号。这里须纠正一个流传甚广的误解:方括号给出的标签并不会变成粗体。 在 itemize 中写 \item[Word],所生成的 PDF 只嵌入正文用的罗马体,全然不见粗体。itemize 与 enumerate 的 \makelabel 定义为 \hss\llap{...},因此标签不过是 在盒内右对齐(超出宽度的部分向左溢出),字体则丝毫未动。唯有 description 才会加粗,而那是因为 \descriptionlabel 里带着 \bfseries。
\theenumi 与 \labelenumi 之别——数的表现与标签的体例
\theenumi 决定 数如何表现——是 1、是 I 还是 a;\labelenumi 则决定 该数在行首如何装点,前后是否加括号或句点。enumerate 每层使用一个计数器:enumi、enumii、enumiii、enumiv;\theenumi 把计数器的值化为字符,\labelenumi 再调用它并添上标点。这是两级的构造。标准文档类的默认值见下表,且并不对称:唯有第 2 层用圆括号,其余都用句点。
| 计数器 | \the… 的默认 | 标签的默认 | 行首印出的形式 |
|---|---|---|---|
enumi | \arabic{enumi} | \theenumi. | 1. 2. 3. … |
enumii | \alph{enumii} | (\theenumii) | (a) (b) (c) … |
enumiii | \roman{enumiii} | \theenumiii. | i. ii. iii. … |
enumiv | \Alph{enumiv} | \theenumiv. | A. B. C. … |
这两级构造的价值在交叉引用上显现。\ref 返回的是剥去 \labelenumi 装饰后的裸表现,也就是 \theenumi。所以,即便你把 \labelenumi 改成 (\theenumi) 使行首印出 (1),\ref 仍返回不带括号的 1;若要写"见步骤 (1)",就得在引用处补上括号,或改用 enumitem 的 ref=。更确切地说,LaTeX 是在 \theenumi 之前加上 \p@enumi 来构造引用字符串的,而标准文档类把 \p@enumii 定义为 \theenumi、\p@enumiii 定义为 \theenumi(\theenumii)。因此对第 2 层条目的 \ref 会返回"1a",连父级编号一并带上。至于计数器本身如何创建与递增,那是计数器与长度那一页的职责。
% change only the representation: the head reads I. II. III. and \ref gives I
\renewcommand{\theenumi}{\Roman{enumi}}
% change only the dressing: the head reads (1) but \ref still gives a bare 1
\renewcommand{\labelenumi}{(\theenumi)}计数器表示命令及其极限
要在 \theenumi 或 \labelenumi 内选定数的外观,可用 LaTeX 标准的六个 计数器表示命令。它们都以计数器名为参数(如 \Roman{enumi}),并按指定体例印出该计数器的值。要紧的是,其中三个 有上限:\alph 与 \Alph 一旦超过 26,\fnsymbol 一旦越出 1–9,都会以 ! LaTeX Error: Counter too large. 中止。一个有 27 项、标签为 label=\alph* 的列表,必定在第 27 项上出错。
| 命令 | 印出的形式 | 范围与极限 |
|---|---|---|
\arabic | 1, 2, 3 …(阿拉伯数字) | 无上限;负数照原样印出(−3 印作 −3) |
\alph | a, b, c …(小写) | 1–26;27 及以上报 Counter too large。0 则悄然为空 |
\Alph | A, B, C …(大写) | 1–26;上限与 \alph 相同 |
\roman | i, ii, iii …(小写罗马数字) | 不会报错,但 0 及以下悄然为空 |
\Roman | I, II, III …(大写罗马数字) | 无上限;4000 印作 MMMM,只是把 M 排开 |
\fnsymbol | * † ‡ § ¶ ‖ ** †† ‡‡(脚注符号) | 仅 1–9;10 及以上、0 及以下均报 Counter too large |
棘手之处在于,有的失败会喊出声,有的却悄无声息。\alph{0} 与 \roman{0} 会一声不吭地返回空串,于是引用一个仍停在 0 的计数器,便造出一份"标签凭空消失"的列表,而线索全无。相比之下 \alph{27} 好歹会以 Counter too large 停下来。\fnsymbol 的九个符号——正是 \@fnsymbol 定义中的那一列——本是脚注用的序列,挪来充作列表标签,到第 10 项就会崩坏。请只把它们留给简短的注记式列表。
用 enumitem 的 label= 来写——由计数器组装标签
重新定义宏固然有力,但当版式需因列表而异时便显笨重。多数情况下 enumitem 的 label= 读起来更清楚,不妨先试它。在 label= 之内,带星号 的 \arabic*、\alph*、\Alph*、\roman*、\Roman* 代表该层当前的计数器。不带星的 \arabic{...} 仍可并用,两者混合便能 由别的计数器组装标签:写 label=\thesection.\arabic*,条目就编成"3.1""3.2",前面冠以节号,且移动该节时会自动跟随。
\usepackage{enumitem}
\setlist[enumerate,1]{label=\Roman*.} % level 1 becomes I. II. III.
\setlist[enumerate,2]{label=(\alph*)} % level 2 becomes (a) (b)
\setlist[itemize,1]{label=$\diamond$} % itemize level 1 becomes a diamond
% a label built from another counter
\begin{enumerate}[label=\thesection.\arabic*, leftmargin=*]
\item numbered 3.1 inside section 3
\end{enumerate}
% label*= accumulates onto the parent label: 1. then 1.1. 1.2.
\begin{enumerate}[label*=\arabic*.]
\item one
\begin{enumerate}[label*=\arabic*.]
\item one-point-one
\end{enumerate}
\end{enumerate}label*= 会接在父级标签之后,嵌套时便累积为 1.、1.1.、1.2.(已实际排版验证)。但要注意,这种串接只作用于行首的显示,交叉引用并不会自动带上父级编号。若想得到"1.a"这样的引用,须用 ref= 明写,如 ref=\theenumi.\alph*。另外,凡是给出较宽的标签,切记补上 leftmargin=*:默认的标签盒只有 20pt,像 \thesection.\arabic* 这样的长标签会向左溢出。若要在全篇生效,则指明种类与层级,如 \setlist[enumerate,1]{...}。
此外,以 \usepackage[shortlabels]{enumitem} 载入宏包,还可像旧的 enumerate 宏包那样简写为 \begin{enumerate}[(1)]。只需把 A、a、I、i、1 置于方括号的首位,便分别表示 \Alph*、\alph*、\Roman*、\roman*、\arabic*。
Too deeply nested——嵌套的真正上限
常听人说 LaTeX 的列表最多只能嵌套四层。这话 只对了一半。上限其实有两个,读一读 latex.ltx 便能看清其构造。itemize 把深度记在 \@itemdepth,enumerate 记在 \@enumdepth——这是 两个各自独立的计数器——各自在超过 3 时调用 \@toodeep。可见四层的限制 只在层叠同一种环境时 才成立。此外,\list 本身还设有一道全局关卡 \ifnum \@listdepth >5,这一道则不问列表是何种类。
实际编译,结果正是如此。层叠五个 itemize,第五个便报 ! LaTeX Error: Too deeply nested.;五个 enumerate 亦然。可若把 itemize 与 enumerate 交替 层叠,六层竟毫无错误,直到第七层才报同样的错——这一次拦下它的是 \@listdepth。可见实用上的上限是"同种四层,交替六层"。不过这只宜当作知识,而非许可:六层深的嵌套几乎不可能读得下去。若确实需要那样的深度,请转向 \subsubsection 之类的标题,或考虑 easylist 宏包,而不是列表。
这个错误的麻烦之处在于,报了错却不会中止排版。\itemize 定义中的深度检查形如 \ifnum ... \@toodeep \else ... \fi,一旦超限,LaTeX 便干脆不开始列表,任正文径直流过。于是造成一种令人费解的损坏:错误只记录一次,PDF 照样生成,而 该层的条目却化作普通正文流出。若在日志里看到 Too deeply nested,纵然 PDF 已经产出,也务必修正相应段落。还有一个后果:为第 5 层定义 \labelitemv 毫无意义,因为检查在抵达该层之前就已把列表挡下。