计数器与长度

LaTeX 有两类可以记住值的“寄存器”(存储位置)。计数器 保存整数,长度 保存尺寸。章、图和公式编号用计数器计数;页边距和盒子的宽度用长度测量。本页介绍 定义并操作 自己的计数器和长度的编程基础。单位本身以及 \hspace/\vspace 这样的空白命令放在另一个页面(见下方链接);这里专注于计数、测量和记忆的机制。

计数器 — 整数寄存器

计数器 是记住一个整数的容器。随着 LaTeX 读取文档,章节开始时它推进 chapter,放置图时推进 figure,并把这些数字打印到标题或题注中。这些是预定义的 标准计数器,你也可以创建自己的计数器。

\newcounter{foo} 创建新计数器。创建后的初始值是 0。名称只能使用字母(不带反斜杠),并且不能与已有名称重复。关键的附加功能是可选父级:\newcounter{foo}[⟨parent⟩] 会让 foo 从属于 parent,因此 每次 parent 前进一步,foo 都会自动重置为 0。这正是小节编号在节改变时重新开始的原因。

有三条命令可以改变值。\setcounter{foo}{3}foo 赋值 为 3;\addtocounter{foo}{2} 在当前值上 2(传入负数即可相减);\stepcounter{foo} 只加 1。\stepcounter 有一个重要副作用:它会 把所有以该计数器为父级注册的子计数器重置为 0。注意,\setcounter\addtocounter 做出的赋值是 全局的,离开组({ } 或环境)后不会恢复。

与它很接近的 \refstepcounter{foo} 同样会前进一步并重置子计数器,但还会 把此时的 foo 设为当前引用目标。因此,紧跟其后的 \label{…} 会指向 foo 的打印值(下面的 \thefoo),而 \ref 会取回这个编号。LaTeX 在推进 sectionequationfigure 时内部使用的正是 \refstepcounter,所以给标题或公式加 \label 才能引用到正确编号。只是在内部推进数字时用 \stepcounter;要创建可引用的“带编号对象”时用 \refstepcounter

latex
\newcounter{trial}            % trial を作成(初期値 0)
\setcounter{trial}{5}         % trial = 5
\addtocounter{trial}{-2}      % trial = 3
\stepcounter{trial}           % trial = 4(子カウンタがあれば 0 に)

取出计数器的“数” — \value

\value{foo} 会把计数器内容取出为 TeX 能用于计算的数字。凡是 LaTeX 期待整数的位置 都可以用它:\setcounter/\addtocounter 的数值位置、\ifnum 条件判断,甚至尺寸计算(如 \value{foo}\parindent)。例如,要让 barfoo 的值一致,可以这样写:

latex
\setcounter{bar}{\value{foo}}        % bar に foo の現在値を代入
\addtocounter{bar}{\value{foo}}      % bar に foo の値をさらに加える

初学者常在这里绊倒:\value 不是用来显示的。要把编号实际 *打印* 到正文中,请使用下一节的 \arabic 这类显示命令,或使用 \thefoo。可以这样区分:\value 给出供算术使用的 原始数字,显示命令给出供人阅读的 字符串。把两者分开,就不会混淆。

打印计数器 — \the 与格式命令

每个计数器都有一组显示命令,用来 把它的值转换成要打印的字符串。你可以选择记法:

命令输出范围 / 备注
\arabic阿拉伯数字(1, 2, 3 …)最常用;允许负值
\roman小写罗马数字(i, ii, iii …)小于 1 时不输出
\Roman大写罗马数字(I, II, III …)小于 1 时不输出
\alph小写字母(a, b, c …)1–26;超出范围会报错
\Alph大写字母(A, B, C …)1–26;超出范围会报错
\fnsymbol脚注符号(∗ † ‡ § ¶ ‖ …)1–9;用于数学模式

\alph/\Alph 映射到 26 个字母,因此值小于 1 或大于 26 时会触发 “Counter too large” 错误。\fnsymbol 会按顺序输出 9 个脚注符号:星号、剑号、双剑号、节号、段落号、平行线、双星号、双剑号、双双剑号;有效范围为 1–9。因为它在数学模式中运行,在正文中请写成 $\fnsymbol{footnote}$,或通过 \thefootnote 使用。

这些命令以计数器名为参数(如 \arabic{page})。但实际排版中打印编号的是每个计数器自动拥有的专用宏 \thefoosection\thesectionfigure\thefigure,依此类推。\renewcommand 重新定义这个 \the…,就能整体改变编号格式。例如,要把节号设为罗马数字,只需这一行:

latex
\renewcommand{\thesection}{\Roman{section}}   % 1, 2, 3 → I, II, III
% 図番号を「節.通し番号」に:図 2.3 のように
\renewcommand{\thefigure}{\thesection.\arabic{figure}}

像第二个例子那样,把另一个计数器的显示混入 \the… 定义,就能生成 “2.3” 这样的复合编号。它之所以有效,是因为图计数器被设置为以节计数器为父级(重置触发器);这说明编号的 外观 与何时归零的 联动关系 是分别决定的。

标准计数器与编号深度

LaTeX 预定义并在文档中自动驱动一组计数器。你也可以用 \setcounter 等自由操作它们,比如在中途重新编号章,或让附录中的图号显示为 A.1

  • 标题层级: partchaptersectionsubsectionsubsubsectionparagraphsubparagraph(可用项取决于文档类)
  • 浮动体和公式: figuretableequation
  • 脚注: footnotempfootnote(minipage 内的脚注)
  • 列表: enumienumiienumiiienumivenumerate 的四级嵌套)
  • 页面: page
  • 控制: secnumdepth(标题编号深入到哪一级)和 tocdepth(目录收录到哪一级)

最后两个略有不同,是 用整数控制编号和列表“深度”的开关。每个标题命令都有级别编号(section 为 1,subsection 为 2,……),当该级别 小于或等于 secnumdepth 的值时才会编号。默认值大约是 2,因此 \setcounter{secnumdepth}{1} 会去掉小节以下的编号,而 \setcounter{tocdepth}{1} 会把目录限制到节。这很好地说明,计数器不仅用于计数,也可以作为控制排版行为的 配置值

长度 — 尺寸寄存器

另一类 长度(length) 是记住一个尺寸的寄存器,如 12pt2cm。计数器保存整数,而长度保存带单位的尺寸,还可以包含伸展和收缩量(plus/minus)。如果同一个尺寸要在文档多处使用,给长度命名比硬编码数字更容易维护。

\newlength{\mylen} 声明新长度。与计数器不同,参数是 带反斜杠的命令名\mylen),初始值为 0pt。基本操作有两条命令:\setlength{\mylen}{2em}(赋值)和 \addtolength{\mylen}{-3pt}(加法;负值表示相减)。创建后,只要是 需要尺寸的位置 都可以使用,例如 \hspace{\mylen}

\textwidth(正文宽度)和 \baselineskip(基线间距)这样的 已有长度 也可以直接参与计算。只要在前面加上系数就能缩放它们:0.8\textwidth 是正文宽度的 80%;长度之间也可以直接相加或相减。若需要更丰富的四则运算,尤其是 除法,请加载 calc 宏包。这样就能写出 \setlength{\x}{\textwidth/3}(正文宽度的三分之一)这样的表达式,直接把尺寸除以整数。

calc 有一个注意点:如果要把尺寸乘以 实数 因子,写法不同于整数除法,需要插入 \real{}(例如 \widthof{词} * \real{0.68})。而且表达式类型必须一致:2cm + 4(尺寸加裸整数)非法,2cm + 4pt 这样两边都是尺寸才可以。

document.tex
\usepackage{calc}
\newlength{\thirdcol}
\setlength{\thirdcol}{\textwidth/3}     % 本文幅の 1/3
\addtolength{\thirdcol}{-1em}            % そこから 1em 引く

测量排版后的内容 — \settowidth 等

长度操作中特别强大的一类命令,会 测量实际排版结果的尺寸 并存入长度。\settowidth{\mylen}{文本} 会在内部排版给定文本,并把它的 宽度 放入 \mylen。同样,\settoheight 测量基线以上的 高度\settodepth 测量基线以下的 深度(三者的目标都必须是先用 \newlength 声明过的长度)。

当你需要 让尺寸贴合内容 时,这尤其有用,比如“画一条与这个标题词正好同宽的线”,或“根据最长的项目名决定标签宽度”。下面的例子测量一个词的宽度,并在下方放一条同样长度的线:

document.tex
\newlength{\wd}
\settowidth{\wd}{重要}        % 「重要」の組版幅を測る
\noindent 重要\par
\rule{\wd}{0.4pt}            % 同じ幅の罫線

同样的“先排版再测量”思路也延续到前面提到的 calc 宏包的 \widthof{文本}/\heightof/\depthof。区别在于:\settowidth 是把结果 赋值 给长度的命令,而 \widthof 可以作为 直接写进表达式。

组合示例 — 自定义计数器与测得的长度

最后看一个同时使用计数器和长度的小例子。我们创建自定义计数器 question,把显示格式设为 “Q1.”、“Q2.”、……,并定义一个简单命令 \question,每次调用时推进并打印编号。因为它使用 \refstepcounter,你可以给每个问题加上 \label,之后用 \ref 引用:

document.tex
\newcounter{question}
\renewcommand{\thequestion}{Q\arabic{question}}
\newcommand{\question}{\refstepcounter{question}\par\noindent\textbf{\thequestion.}\ }
\begin{document}
\question 最初の問い。\label{q:first}
\question 次の問い。
問い~\ref{q:first} を参照。   % → 問い Q1 を参照
\end{document}

\renewcommand 定义 \thequestion 后,编号格式(Q + 阿拉伯数字)集中在一个地方,日后修改很容易。长度方面同样适用这种思路:以正文宽度(\textwidth)为基准构造相对尺寸,或用 \settowidth 测量内容后决定尺寸,这样即使纸张大小或页边距改变,文档也能保持比例。不要把固定值硬编码到各处;给它们命名并集中管理。这就是把计数器和长度当作编程工具使用的诀窍。