术语表与符号表

每一份投稿规范都写着同一句话:缩略语要在首次出现时写出全称,此后用缩写即可。靠人手几乎守不住。挪动一节,「首次」的位置就跟着移动;漏改了关键的那一处,审稿人一定会发现。LaTeX 的 glossaries 宏包(以及后继的 glossaries-extra)把这条规则交给机器执行。术语或缩略语在导言区定义一次,正文中只写 \gls{key}:首次使用会自动展开,顺带只有真正用过的词条才会排好序出现在文末的术语表里。本页沿着完整路径走一遍——定义词条、\newacronym、运行 makeglossaries\printglossary——并先行排除术语表变成空白的四种原因,其中三种连警告都不会给

定义一次,随处调用:newglossaryentry 与 gls

在导言区写 \newglossaryentry{key}{name=..., description=...},正文中用 \gls{key} 调用。第一个参数 key 是你自定的标签;name 是会被印出来的文字,description 是将出现在术语表里的说明。要记住的关键是:\gls 同时做两件事——把 name 插入当前位置,并向辅助文件写入一条记录,声明这个词条属于术语表。因此,只定义却从未用 \gls 调用过的词条根本不会出现。只列出用过的词条是设计如此,而非缺陷。

各种变体只在命令开头的字母上有别。句首用 \Gls{key};复数用 \glspl{key};两者兼有则用 \Glspl{key}。自动生成的复数只是在 name 末尾加个 “s”,所以像 matrices 这样的不规则形式必须用 plural key 明确写出。若正文中的写法要不同于显示名,就设 text;相关符号放进 symbol key,用 \glssymbol{key} 调出;只想插入说明时用 \glsdesc{key}。若说明长到跨越多个段落,改用 \longnewglossaryentry。另外,调用不存在的 key 会以 ! Package glossaries Error: Glossary entry ... has not been defined. 中止——拼错时不被默默忽略,在这里是优点。

latex
\usepackage{glossaries}
\makeglossaries              % opens the glossary files -- required

\newglossaryentry{set}{%
  name={set},
  description={a collection of distinct objects}%
}
\newglossaryentry{matrix}{%
  name={matrix},
  plural={matrices},      % irregular plural, spelled out
  description={a rectangular array of numbers}%
}

\begin{document}
\Gls{set} theory studies a \gls{set}; linear algebra studies \glspl{matrix}.
\printglossaries
\end{document}
命令输出用途
\gls{set}set普通引用;同时也是把词条登记进术语表的动作
\Gls{set}Set句首把首字母大写
\glspl{matrix}matrices复数;默认是 name 加 s,可用 plural key 覆盖
\Glspl{matrix}Matrices复数并首字母大写
\glsdesc{set}a collection of distinct objects只插入 description 字段的内容
\glssymbol{sigma}σ调出存放在 symbol key 中的符号

把缩略语交给机器:newacronym 与首次展开

\newacronym{key}{short}{long} 定义,之后只写 \gls{key} 即可。short 是缩写(例如 SVM),long 是全称(support vector machine)。同样写两次 \gls{svm},第一次输出「support vector machine (SVM)」,之后都只输出「SVM」。这正是机器接手那条人手守不住的规则之处:首次使用标志按词条跟踪,并且遵循处理的先后顺序,所以挪动一节时展开的位置也随之移动。稿件重排之后不会自相矛盾。

若希望从某处起再次写出全称——比如某一章要能独立阅读——就用 \glsreset{key};要一次重置全部词条则用 \glsresetall。若要把缩略语单独汇成一份列表,用 \usepackage[acronym]{glossaries} 加载宏包:这样就有了两份互相独立的列表,术语表和缩略语表,各自配有一组辅助文件。另外,如果同时加载了 glossaries-extra\newacronym 会成为带 category=acronym\newabbreviation 的别名——所以若是新开项目,直接写 \newabbreviation 可以立刻用上丰富的缩略语样式。

latex
\usepackage[acronym]{glossaries}   % a second, separate list
\makeglossaries

\newacronym{svm}{SVM}{support vector machine}

\begin{document}
\gls{svm} is a classifier.   % -> support vector machine (SVM)
Another \gls{svm} follows.   % -> SVM

\glsreset{svm}               % start a chapter that must stand alone
\gls{svm} again in full.     % -> support vector machine (SVM)

\printglossary[type=main,title={Glossary}]
\printglossary[type=\acronymtype,title={Acronyms}]
\end{document}

构建:makeglossaries 把 .glo 变成 .gls

LaTeX 只负责记录词条,既不排序也不排版。第一次运行时,\makeglossaries 会产生一个 .ist 文件——写着排序规则的样式文件——凡被 \gls 调用过的词条则积存在 .glo 中。这时插入外部程序 makeglossaries,它会亮出底牌:运行后会打印出 makeindex -s mydoc.ist -t mydoc.glg -o mydoc.gls mydoc.glo。这正是制作索引所用的同一个 makeindex。排好序的 .gls 一旦生成,再跑一次 LaTeX 把它读进来即可。

terminal
pdflatex mydoc       # writes mydoc.glo (and mydoc.ist)
makeglossaries mydoc # sorts it: no file extension here
pdflatex mydoc       # reads mydoc.gls, prints the glossary

# what makeglossaries actually runs, once per glossary type:
#   makeindex -s mydoc.ist -t mydoc.glg -o mydoc.gls mydoc.glo
#   makeindex -s mydoc.ist -t mydoc.alg -o mydoc.acr mydoc.acn

有两份术语表,就有两组文件。默认术语表走 .glo.gls,记录写在 .glg;加上 acronym 选项后,缩略语表使用 .acn.acr(记录 .alg),于是 makeglossaries 会调用 makeindex 两次。正是这份「知道一共有几份列表,并按需运行相应次数」的职责,让我们插入 makeglossaries 而不是自己敲 makeindex。该脚本用 Perl 写成,所以在没有 Perl 的环境(Windows 上很常见)里改用 makeglossaries-lite:同样的工作,实现为由 texlua 运行的 makeglossaries-lite.lua

术语表一片空白时:四种原因,其中三种毫无声息

最常见的原因是忘了运行 makeglossaries,而这种失败几乎不给你任何线索。没有 .gls,术语表就连标题一起不出现——不是留下一个空框,而是那里什么都不排。既无错误也无警告,只在日志深处留下一行 No file mydoc.gls.。这与索引里忘记 makeindex 是同一个陷阱,而且伪装得很好:\gls 本身从第一次运行起就能正确展开,所以只看 PDF 正文时,一切都像在正常工作。

  • 没有运行 makeglossaries 没有 .gls,术语表连同标题一起不出现。没有警告,日志里只有 No file mydoc.gls.
  • 导言区缺少 \makeglossaries 输出文件根本没被打开,连 .glo 都不会生成,同样什么都不印。没有警告。
  • 只定义了词条却没用 \gls 调用。 未使用的词条不会被记录,因此不会列出。这是设计如此——想让某个词进术语表,就在正文里至少提到一次。
  • 唯一会明确告诉你的,反倒是相反的错误。 写了 \makeglossaries 却忘了 \printglossary,会得到 Package glossaries Warning: No \printglossary or \printglossaries found. (Remove \makeglossaries if you dont want any glossaries.) This document will not have a glossary.

还有一种组合会一声不吭地出问题。如果使用 hyperref要在 hyperref 之后加载 glossaries——这是「hyperref 最后加载」这条经验之谈为数不多的例外之一。该宏包的入门指南明确这样写着,而顺序弄反时不会有任何警告:术语表里的链接和页码只是悄悄地坏掉。请按下面的顺序排列。

latex
\usepackage[colorlinks]{hyperref}
\usepackage{glossaries}   % after hyperref, not before
\makeglossaries

% put the glossary into the table of contents as well:
% \usepackage[toc]{glossaries}

输出:printglossary 的标题、类型与进入目录

\printglossaries 会输出你准备好的所有列表,\printglossary 只输出一份。选哪个取决于是否需要选项:若要让每份列表有自己的标题或样式,就传入选项,例如 \printglossary[type=main, title={术语表}];否则一行 \printglossaries 就够了。标题用词本身存放在 \glossaryname 中,可用 \renewcommand 替换。

这些标题没有编号,所以默认不会进入目录。把宏包写成 \usepackage[toc]{glossaries} 加载,就会自动列入目录,比为每份术语表各写一行 \addcontentsline 更可靠。外观本身用 \setglossarystyle{...} 切换:list(默认)基于 description 环境;altlist 让词条独占一行、说明缩进其下;long 系列则整个排成表格。说明越长,altlistlong 系列在可读性上的回报就越明显。

现代配置:glossaries-extra 与 bib2gls

glossaries 的首版发布于 2007 年 5 月 16 日,由 Nicola Talbot 作为旧 glossary 宏包的后继推出。同一位作者随后在 2015 年发布 glossaries-extra,2017 年发布 bib2gls。这套组合的思路完全借自文献管理:把术语存放在 .bib 文件里,bib2gls 只挑出正文中实际用到的那些,排好序再读入——正如 biber 只取被引用的文献。选择与排序这两件原本属于 makeindex / xindy 的活,由一个程序全部接手。

关键在 record 选项。写成 \usepackage[record]{glossaries-extra} 加载后,makeindex / xindy 的索引会被关闭,转而向 .aux 写入形如 \glsxtr@record{set}{}{page}{glsnumberformat}{1} 的行。bib2gls 读取这些记录,只把需要的条目写回 .glstex。正因如此,第一次运行时尚未定义任何条目才是正常的——所以 glossaries-extra 把未定义条目从错误降级为警告。第一遍出现一串 Package glossaries-extra Warning: Glossary entry ... has not been defined 完全在意料之中。这与纯 glossaries 在同样场合直接中止形成对照,两种选择都各自自洽。

terms.bib
@entry{set,
  name = {set},
  description = {a collection of distinct objects}
}

@abbreviation{svm,
  short = {SVM},
  long  = {support vector machine}
}

@symbol{sigma,
  name = {\ensuremath{\sigma}},
  description = {standard deviation}
}
latex
\usepackage[record]{glossaries-extra}
\GlsXtrLoadResources[src={terms}]   % terms.bib, without the extension

\begin{document}
\gls{set} and \gls{svm} are used here.
\printunsrtglossary                 % already sorted by bib2gls
\end{document}

文档里的写法几乎没有变化。用 \GlsXtrLoadResources[src={terms}] 读取 .bibsrc 是不带扩展名的文件名),术语照旧用 \gls{set} 调用。不同的是输出命令:既然 bib2gls 已经排好序,就改用 \printunsrtglossaryunsrt 即 unsorted,意为「原样输出」)。构建时用 bib2gls 取代 makeglossaries;加上 --group 会生成按首字母分组的小标题,pdflatex 也可换成 xelatexlualatex。安装时有一点要留意:bib2gls 用 Java 写成,因此需要 Java 运行环境(至少 Java 8)。TeX Live 中的命令其实是一个启动 .jar 的 shell 脚本,所以在没有 Java 的机器上,一运行就会发现。

terminal
pdflatex mydoc
bib2gls --group mydoc   # reads mydoc.aux, writes mydoc.glstex
pdflatex mydoc

只要符号表的话,用 nomencl

若只是要在论文前面放一份符号表,用 glossaries 当然可以,但轻量的 nomencl 环节更少。在导言区放 \usepackage{nomencl}\makenomenclature,在符号首次出现处用 \nomenclature{$g$}{gravitational acceleration} 标记,再在想输出列表的位置写 \printnomenclature 即可。符号属于数学内容,所以要用 $...$ 包住。构建同样借用 makeindex\makenomenclature 产生 .nlo,用附带的样式 nomencl.ist 排序生成 .nls,再跑一次 LaTeX 读入。

terminal
pdflatex mydoc
makeindex mydoc.nlo -s nomencl.ist -o mydoc.nls
pdflatex mydoc

排序依据的是符号的输入本身,逐字符比较。写下 $\sigma$,排序键就是字符串 $\sigma$,其中的美元符与反斜杠在字符编码上都排在所有字母之前。实测下来,σ 会排在 g 和 m 的前面。因此才有可选参数用来提供自定的排序键:在 \nomenclature[g-sigma]{$\sigma$}{...} 中,参与排序的是 g-sigma,而印出来的仍是符号本身。顺带一提,\nomenclature 前一行的行末应加 %:符号周围混入多余空格会打乱排序。

latex
\usepackage{nomencl}
\makenomenclature
\renewcommand{\nomname}{List of Symbols}
% \usepackage[intoc]{nomencl}  % also list it in the contents

\begin{document}
Let $g$ be gravity.%
\nomenclature{$g$}{gravitational acceleration}%
A mass $m$ feels $F = mg$.%
\nomenclature{$m$}{mass of the object}%
\nomenclature[g-sigma]{$\sigma$}{stress}% sort key, not the symbol

\printnomenclature
\end{document}

标题默认是英文 “Nomenclature”,可用 \renewcommand{\nomname}{...} 替换。若要列入目录,就用 \usepackage[intoc]{nomencl} 加载。还有一些选项能自动为每一条加注:refpage 追加 “, page n”,refeq 追加 “, see equation (n)”。若想把物理常数与变量分开,可以利用刚才那个排序键的首字符重新定义 \nomgroup,把列表拆成各自带标题的小组。

  • 术语和缩略语的术语表用 glossaries 首次展开、复数、大写都替你照顾好。
  • 新项目建议 glossaries-extra 搭配 bib2gls 术语写在 .bib 里,用 \printunsrtglossary 输出用过的那些——只需确认机器上有 Java。
  • 若只要数学符号列表,用 nomencl\nomenclature 标记,跑一次 makeindex 就完事。
  • 这些都需要额外一遍编译。 中间插入外部程序(makeglossaries / bib2gls / makeindex),然后再跑一次 LaTeX。忘了也没人会提醒你。