当你被 LaTeX 卡住时,握有答案的人多半就在 tex.stackexchange.com 上。只是这个社区有入场费,而这份费用不是礼貌,而是 最小可运行示例(minimal working example,简称 MWE)。这件事有多认真?认真到 TeX Live 里专门收录了一个叫 mwe 的宏包,唯一任务就是让最小示例更容易分享;而 texdoc minexample 会打开一本 21 页的小册子,通篇只讲怎么做出一个最小示例。本页要谈的是:LaTeX 的问题该带去哪里——TeX Stack Exchange、Usenet 新闻组 comp.text.tex 留下的遗产、TUG 与各国用户组、宏包的问题追踪器——以及怎样提问才会有人回答,也就是把 300 页文档削到二十行的手艺。
提问之前,先搜 tex.stackexchange.com
大多数 LaTeX 问题都已经有人用同样的话问过了。 TeX Stack Exchange(tex.stackexchange.com)建于 2010 年 8 月——这个日期不是道听途说,而是记在 LaTeX 团队自己的通讯里。随 TeX Live 一起发行的《LaTeX3 News》第 5 期(2011 年 1 月)写道:TeX Stack Exchange 问答网站已建立并迅速成长,截至撰稿时约有 2,800 人提出了 2,600 个问题、共 5,600 条回答,每天有 2,200 位用户来访。在本机输入 texdoc l3news 就能打开同一页。十几年过去,数字多了两位,但真正增长的是历史问题的存量。搜索有一个诀窍:不要用自己的话描述问题,而要原封不动地贴上错误信息。像 ! Undefined control sequence 或 ! Missing $ inserted 这样的一行,就是最好的搜索关键词。
在那之前的三十年,TeX 讨论的重心在 Usenet 新闻组 comp.text.tex(德语圈另有 de.comp.text.tex)。它当年有多核心,看看那个时代书籍的致谢就知道。Victor Eijkhout 在《TeX by Topic》(Addison-Wesley,1991)的致谢中感谢了讨论列表 TeXhax、荷兰的 TeX-nl 以及 comp.text.tex 的参与者,说他们的提问与回答给了他许多思考的素材。这本书随 TeX Live 一起发行,texdoc texbytopic 就能读到——连致谢一起读,正好可以亲眼看见重心的移动。新闻组至今仍在,但今天要问 LaTeX 问题,第一选择是 TeX Stack Exchange。旧帖子仍会出现在搜索结果里,遇到时请务必确认它是哪一年的。
最小可运行示例(MWE)到底是什么
MWE 就是能复现问题的、最短的「完整」文档——这里的「完整」是严格意义上的。Nicola L C Talbot 的《Creating a LaTeX Minimal Example》(2014 年,随 TeX Live 发行,texdoc minexample)在开篇就强调:最小示例不得包含任何与问题无关的宏包或代码,但必须包含文档类和 document 环境。也就是说,它不是片段,而是对方可以原样保存、直接用 pdflatex 编译的东西——这正是名字里「working」的含义。反过来说,如果贴的是没有 \begin{document} 的三行代码,回答者的第一句多半是「请给一个完整的例子」,白白多一个来回。
% A minimal working example: complete, compilable, and as short as it can be.
% Nothing here that does not bear on the problem being reported.
\documentclass{article}
\usepackage{booktabs}
\begin{document}
\begin{tabular}{ll}
\toprule
left & right \\
\bottomrule
\end{tabular}
\end{document}示例里需要插图时最容易卡住:你不能把自己的照片寄过去,就算寄了对方环境里也没有。这正是 mwe 宏包的用处。写下 \usepackage{mwe} 会载入 graphicx,并让一组标准图片可以从 TeX 树中直接取用——example-image、example-image-a、example-image-16x9、example-grid-100x100bp 等等。凡是装了 TeX Live 的人手上都有这些文件,因此写着 \includegraphics{example-image} 的示例在谁的机器上都能编译。同理,需要大段正文时可用 lipsum 的 \lipsum[1-3] 或 blindtext 的 \blindtext(若装有 lipsum,mwe 会自动载入它)。不需要附件的示例,光凭这一点就能更快得到回答。
把 300 页削到二十行——building up 与 hacking down
路只有两条,Talbot 称之为 building up(自下而上搭)与 hacking down(自上而下砍)。搭,是从 \documentclass{article} 和一个空的 document 环境开始,一次加一样东西,直到问题出现。砍,是从真实文档的副本出发,一直删到问题消失。稿子短就搭,有 300 页就砍更快——但砍的时候别一行一行来,要一半一半地删。把导言区的前一半注释掉,如果问题还在,那前一半就一次性洗清了嫌疑。再把剩下的一半对半砍,如此十几轮,几百行就变成寥寥数行。这就是二分查找,对 \include 进来的章节同样适用。
- 先做副本。 绝不要动原始的
.tex,所有删改都在复制品上进行。 - 先扔掉正文。 去掉
\include的章节、插图、表格和参考文献,只在\begin{document}之后留下出问题的那一行。 - 导言区一半一半地删。 问题还在,说明删掉的那半是无辜的;问题消失,就转而怀疑刚删掉的部分,再对半分。
- 展开自己写的宏。 把
\newcommand换成它的定义内容,就能分清「问题在我的宏里」还是「问题在宏包里」。 - 试着把文档类换成
article。 如果换了就不再复现,那文档类就是原因——这本身就是有价值的发现,报告时把这一点一并写上。 - 每删一次就编译一次。 最常见的失误是删过头,却没察觉问题早已不再复现,还继续往下删。
削完之后,最后附上版本信息。这不必手写:在 \documentclass 之前放一行 \listfiles 再编译,.log 末尾就会多出一节 *File List*,把载入的每个文件连同日期和版本一并列出。再写上引擎(pdflatex / xelatex / lualatex)与发行版(TeX Live 2024、MiKTeX、Overleaf),回答者基本就能重建你的环境。错误信息不要概括——把以 ! 开头的那行和随后几行原样贴出来。 「好像报了个错」这样的描述,信息量一定少于原文那一行。
% \listfiles before \documentclass, then look at the end of the .log:
*File List*
article.cls 2023/05/17 v1.4n Standard LaTeX document class
size10.clo 2023/05/17 v1.4n Standard LaTeX file (size option)
booktabs.sty 2020/01/12 v1.61803398 Publication quality tables
***********Stack Exchange 之外——TUG、各国用户组与问题追踪器
TUG(TeX Users Group) 是 1980 年成立的国际性非营利会员组织,支撑着包括 TeX Live 在内的开发,出版期刊 TUGboat,并举办年度会议。TUGboat 不只是读物,这一点在本机就能确认:投稿用的文档类 ltugboat.cls 就收录在 TeX Live 中(版权行写着 “Copyright 1994-2023 TeX Users Group”,维护者正是 TUG 本身),执行 texdoc tugboat 便会打开面向作者的指南 ltubguid.pdf。换句话说,若你想把学到的 TeX 心得写出来发表,排版工具早就装好了。
TeX 世界同样由各国的用户组撑着。TeX Live 官方指南在致谢中感谢 TUG、德语圈的 DANTE e.V.、荷兰的 NTG 和波兰的 GUST 提供了必要的技术与行政基础设施,并加上一句「请加入离你最近的 TeX 用户组」,指向 tug.org/usergroups.html。西班牙语圈的 CervanTeX 也向 TeX Live 贡献了一份 FAQ,用 tlmgr info es-tex-faq 就能确认。日语圈则由 日本 TeX 开发社区(texjporg) 维护 pLaTeX/upLaTeX、jsclasses(最初出自奥村晴彦)、dvipdfmx 的日文支持、gentombow、ptex2pdf 等,并运营 TeX Wiki(texwiki.texjp.org);用日语提问时,奥村先生的 TeX 论坛(okumuralab.org/tex/)是事实上的窗口。此外还有 latex.org 论坛、邮件列表 [email protected],以及 Reddit 上的 r/LaTeX。
当你确信这不是提问而是缺陷时,去处就不同了。宏包本身的问题该交给作者的问题追踪器——而地址不用去找,本机就有。若 tlmgr info <宏包名> 的输出里有 cat-contact-bugs 或 cat-contact-repository 一行,那就是官方的报告地址(例如 tlmgr info mwe 会给出 GitHub 的 issue 页面)。而 LaTeX 本体(内核)的缺陷则交给 LaTeX Project(latex-project.org),此时要用 latexbug 宏包。它的用途是给缺陷归类,LaTeX 团队要求随缺陷报告寄出的测试文件都要载入它:载入之后就能判定这个缺陷究竟属于内核还是第三方宏包。寄错地址的报告,等于没送到。
| 去处 | 适合的事项 | 备注 |
|---|---|---|
tex.stackexchange.com | 「该怎么写」「为什么报错」这类问题 | 2010 年 8 月开设;先搜索,再带着 MWE 提问 |
texwiki.texjp.org | 日文环境的安装、配置与和文字体 | 由日本 TeX 开发社区运营 |
[email protected] | 偏讨论性的话题、历史沿革方面的咨询 | TUG 的邮件列表;不是即答型场所 |
cat-contact-bugs | 某个具体宏包的缺陷与功能请求 | tlmgr info <宏包名> 会告诉你地址 |
latexbug | LaTeX 内核本身的缺陷 | 在测试文件里载入它,它会帮你判定该寄给谁 |
怎样写出能得到回答的问题
只需要四样东西:简短的症状说明、MWE、错误原文,以及你已经试过什么。 Talbot 在上文那本小册子里建议:描述要简洁,列出你为追查问题而尝试过的方法,不要长篇讲述整个项目——信息太多反而会让人不想读。她还点明了一个容易被忘记的前提:没有人拿报酬,也没有人有义务回答,所以行文不该听起来像要求或指责。这条建议写于 2014 年,至今仍原样随 TeX Live 发行,这件事本身就说明了这个社区的气质。再补一句:别忘了写清你想达成什么。如果读者只看到失败的做法,就没人能提出你没想到的更简单的路。