出题老师最终走向 LaTeX,理由通常只有一个:试卷和答案分成两个文件管理,迟早会对不上。改了题目忘了改答案,改了分值忘了改总分。exam 类用结构解决了这件事:答案就写在题目旁边,有没有一行 \printanswers,决定同一份源文件产出试卷还是答案两种 PDF。实测下来:带答案 2 页,空白卷 3 页——同一个文件,出来的是两份不同的文档。本页讲这个 exam 类,以及另一类结构化文档——学位论文:report、book、memoir 的区别,哪些类里 \frontmatter 根本没有定义,以及最要紧的一条建议:别跟学校的模板较劲。
exam 类的骨架——\question、parts 与分值
exam 类的结构是三层嵌套:questions 环境里放 \question,其下的 parts 环境里放 \part,再下面 subparts 环境里放 \subpart。分值写在方括号里——\question[10]——加上类选项 addpoints,它就会自动为你求和。题数、总分、总页数可以在正文中用 \numquestions、\numpoints、\numpages 引用,于是「共 4 题,满分 30 分,共 3 页」这样的说明再也不用手工改。选择题有四个环境:choices 竖排,oneparchoices 排成一行,checkboxes / oneparcheckboxes 带可勾选的方框。正确选项不写 \choice 而写 \CorrectChoice,正是它让后面讲的答案显示得以运作。
分值印在哪里可以细致地选。默认跟在题号之后,但 \pointsinmargin / \pointsinrightmargin 能把它挪到页边,\pointsdroppedatright 则把右边距变成记录扣分的栏位。把「points」换成别的词,只需重定义 \pointname 一行。页眉页脚在选择 \pagestyle{headandfoot} 之后按三段书写——\header{左}{中}{右} 和 \footer{左}{中}{右}——而 \firstpageheader / \runningheader 让首页与其余页不同。印出「第 2 页 / 共 3 页」是这个类的标准配置,光凭这一点就能认出少了一张纸的答卷。
\documentclass[addpoints,12pt]{exam}
\pagestyle{headandfoot}
\header{Calculus I --- Midterm}{}{Page \thepage\ of \numpages}
\begin{document}
\begin{center}\gradetable[v][questions]\end{center}
\begin{questions}
\question[10] Differentiate $f(x)=x^3$.
\question[5] Pick the correct answer.
\begin{oneparchoices}
\choice wrong \CorrectChoice right \choice wrong
\end{oneparchoices}
\question Answer both parts.
\begin{parts}
\part[3] State the definition.
\part[7] Prove the claim.
\end{parts}
\end{questions}
\end{document}\printanswers 改变了什么——同一份源文件产出试卷与答案
\printanswers 是「显不显示答案」的开关,但真正有意思的是答案被隐藏时,取而代之的是什么。solution 环境只管印或不印;改写成 solutionorlines[2.5in],显示答案时给出答案,不显示时则留出 2.5 英寸的横线。同样的思路还有 solutionorbox(方框)、solutionordottedlines(虚线)、solutionorgrid(方格纸)。把一份六道题、每题都包在 solutionorlines[2.5in] 里的文档按两种设置各编译一次,结果是 \printanswers 版 2 页,\noprintanswers 版 3 页。答案文字在前者出现 6 次,在后者一次都没有。多出来的那一页,正是被隐藏的答案换成的书写空间。
% One source, two documents.
% \printanswers -> the key (measured: 2 pages, 6 solutions printed)
% \noprintanswers -> the paper (measured: 3 pages, 0 solutions printed)
\documentclass[addpoints,12pt]{exam}
\printanswers % comment this line out to get the blank paper
\begin{document}
\begin{questions}
\question[10] State the definition and prove the claim.
\begin{solutionorlines}[2.5in]
$f'(x)=3x^2$ by the power rule.
\end{solutionorlines}
\end{questions}
\end{document}实际使用中,记住有两种切换方式会很方便:在源文件里写 \printanswers,或者传入类选项 [answers]。输出完全相同——在同一份文档上核对,两者都是 2 页、6 处答案。走选项这条路可以完全不动正文,于是 latexmk 规则或 Makefile 就能通过 \PassOptionsToClass 一次产出两种 PDF。此外还有 cancelspace 选项:既不印答案,也不留答题空间——它会完全忽略 solutionor… 环境的方括号参数,给出只有题目的清单,正适合把往年真题当作练习纸发下去。注意 cancelspace 在显示答案时完全不起作用。
| 命令 / 环境 | printanswers 时 | noprintanswers 时 |
|---|---|---|
solution | 印出答案 | 什么也不印 |
solutionorlines | 印出答案 | 指定高度的横线 |
solutionorbox | 印出答案 | 指定高度的空方框 |
solutionorgrid | 印出答案 | 指定高度的方格纸 |
\fillwithlines | 无论开关如何都印横线 | 同左 |
cancelspace | 无效果 | 忽略方括号参数,不留空间 |
印出评分表——\gradetable 与 \pointsonpage
\gradetable[v][questions] 一行,就能在封面印出题号、分值与得分栏排成的评分表。第一个参数是方向(v 竖排、h 横排),第二个是坐标轴:questions 按题一行,pages 按页一行。按页排的表在多人分工阅卷时最能发挥作用——它正好对应「第 3 页归谁批」的做法。求和相关的命令也齐备:\pointsofquestion{3} 返回第 3 题的分值,\pointsonpage{2} 返回第 2 页的总分。配合 addpoints 选项使用,改一个分值,表格和总分会同时跟着变。不必手工做加法,正是这项功能的全部价值。附加题有 \bonuspoints 以及对应的附加分表格。
这个 exam 类做得有多认真,从手册的厚度就能看出来。TeX Live 2024 里的 examdoc.pdf 长达 135 页——一个只为出试卷而生的类,配了一本 135 页的说明书。作者是 Philip Hirschhorn,韦尔斯利学院数学系教授,以著作 Model Categories and Their Localizations 闻名的拓扑学家。手册的版权行列着修订年份 1994、1997、2000、2004、2008、2011、2015、2017、2021、2023——也就是说,这是一位数学家为自己的教学、持续维护了将近三十年的类。TeX Live 2024 收录的是 v2.704,日期为 2023 年 7 月 9 日。
学位论文模板——别和学校发下来的那份较劲
先说结论:如果学校发了类或模板,就用它,并且尽量少改。理由不在排版,而在流程。页边距、行距、封面措辞、图表编号样式,在多数机构都被明文写进了提交要求,偏离了的论文不看内容就会被退回——而且退回的时间通常就是截稿当天。单看 TeX Live 2024,名字里含 thesis 或 diss 且提供 .cls 的目录就有 64 个:mitthesis、ucbthesis、uiucthesis、thuthesis、njuthesis、ut-thesis、york-thesis……。而那些从未上传到 CTAN 的本地模板数量更为庞大,其中多数以「从学长手里传下来的 .cls」的形式流通。
那么这类模板的内里究竟是什么?绝大多数情况下,就是在 book 或 report 之上罩了一层规定好的封面与页面尺寸的类。所以 \chapter、\tableofcontents 在其中都照常工作;反过来说,模板看起来坏掉时,毛病通常出在底座那一层。如果没有硬性规定、要自己选,有分章的论文用 report 或 book;想更细致地控制版式,就用 memoir(TeX Live 2024 中为 v3.8.2,日期 2024 年 1 月 26 日,它自称是「configurable book, report, article document class」)或 KOMA-Script 的 scrbook / scrreprt。下一节就从「各自到底定义了什么」来看它们的差别。
report 与 book 的区别——单双面,以及章从哪一页开始
区别就写在默认选项里。book.cls 是 \ExecuteOptions{letterpaper,10pt,twoside,onecolumn,final,openright},report.cls 同一行则是 oneside 与 openany。也就是说,book 按双面排版,每章必定从奇数页(右页)开始,为此有时会插入空白页;report 按单面排版,章直接从下一页开始。若论文要装订成册提交,book 的行为才是对的;若以 PDF 提交、并不预设双面打印,report 更直截了当。article 根本没有 \chapter——需不需要 \chapter,正是选类时的第一个分岔口。
| 文档类 | 默认单双面 | 章的起始页 | frontmatter |
|---|---|---|---|
article | oneside | 根本没有 \chapter | 未定义 |
report | oneside | openany(下一页) | 未定义 |
book | twoside | openright(奇数页) | 已定义 |
memoir | 同 book(可配置) | openright(可改) | 已定义 |
scrbook | twoside | openright | 已定义 |
scrreprt | oneside | openany | 未定义 |
\frontmatter 为何报 ! Undefined control sequence,以及这三个命令实际做了什么
\frontmatter、\mainmatter、\backmatter 只在 book 系的类里有定义。在 \documentclass{report} 下写 \frontmatter,会得到 ! Undefined control sequence.,并指向 l.3 \frontmatter。检索类文件也证实了这一点:字符串 frontmatter 出现在 book.cls、memoir.cls、scrbook.cls 中,而在 article.cls、report.cls、scrreprt.cls 中没有。「学校模板里 \frontmatter 不起作用」的求助大多就是这个原因——那份模板的底座是 report。如果不需要章编号方面的行为,手写 \pagenumbering{roman} 与 \pagenumbering{arabic} 就能得到同样的效果,因为这三个命令的内容差不多就只有这些。
那么它们的内容究竟是什么?在 book.cls 里:\frontmatter 执行 \cleardoublepage,置 \@mainmatterfalse,并 \pagenumbering{roman};\mainmatter 执行 \cleardoublepage,置 \@mainmattertrue,并 \pagenumbering{arabic}。而 \backmatter 只是换页并置 \@mainmatterfalse,完全不碰页码。这一点常被误传;凡是说「后置部分会改变页码」的讲解都是错的。\@mainmatter 为假时真正改变的是 \@chapter 的行为:不推进章计数器、标题中不印编号、目录条目不带 \numberline。也就是说,在前置与后置部分,章不带编号,但仍会进目录——致谢和参考文献没有编号却能从目录跳转,论文写作的这一整套惯例,就靠这一个开关实现。
% Measured page labels from this exact file (book, 12pt):
% front matter -> i, ii, iii, iv (roman, chapters unnumbered but in the ToC)
% main matter -> 1, 2, 3, 4 (arabic, "Chapter 1", "Chapter 2")
% back matter -> 5 (still arabic; chapter unnumbered)
\documentclass[12pt]{book} % or your university's class
\begin{document}
\frontmatter
\chapter{Acknowledgements}
Thanks.
\tableofcontents
\mainmatter
\include{chapters/intro}
\include{chapters/method}
\backmatter
\chapter{Bibliography}
Refs.
\end{document}实务——把章拆成文件,别在截稿前夜把它弄坏
写长论文首先该做的,是按章拆成文件,再用 \include 汇总。\include 会在每个文件前后另起一页,正好契合章的边界;而写上 \includeonly{chapters/method} 之后,就能只排一章而不排其余,并且页码与交叉引用仍然正确。在一篇 300 页的论文里只想改第 7 章时,有没有这一招,等待时间会差一个数量级。项目拆分和大型文档处理各有专门页面,请沿 related 前往。还有一个临近截稿时很管用的习惯:动版式之前先开分支。想把页边距再收紧 1 毫米的冲动,几乎无一例外出现在交稿前夜。
- 摘要写在
abstract环境里——但在 book 系的类中,许多指定模板要求用\chapter*{Abstract},所以先看学校发的格式说明。 - 若要一个不带编号但仍出现在目录里的章,在
\chapter*{...}之后写上\addcontentsline{toc}{chapter}{...}。在\frontmatter之内,直接用\chapter就已经是这个效果。 - 被
\include引入的文件里不能写\documentclass或\begin{document};写了就会得到! LaTeX Error: Can be used only in preamble. - 提交前务必完整重编一次。 残留的
.aux会让一个你已经删掉的交叉引用继续存活,而编译照样通过。 - 如果确实改动了学校的类,留一条注释说明改了什么。三个月后的你和下一届的学弟学妹都会读到它。
最后说一个这两类文档共有的格局。试卷和论文,都是内容与体裁分别由不同的人掌管的文档:试卷是出题者与阅卷者,论文是作者与审查机构。LaTeX 在这一领域之所以强,不是因为排版好看,而是因为那种分离可以直接表达为源文件的结构。一行 \printanswers 把试卷与答案分开,一行 \frontmatter 把带编号的章与不带编号的章分开。能否把一个分支折叠成一个开关,正是在截稿前夜见分晓的地方。