考试与学位论文

出题老师最终走向 LaTeX,理由通常只有一个:试卷和答案分成两个文件管理,迟早会对不上。改了题目忘了改答案,改了分值忘了改总分。exam 类用结构解决了这件事:答案就写在题目旁边,有没有一行 \printanswers,决定同一份源文件产出试卷还是答案两种 PDF。实测下来:带答案 2 页,空白卷 3 页——同一个文件,出来的是两份不同的文档。本页讲这个 exam 类,以及另一类结构化文档——学位论文reportbookmemoir 的区别,哪些类里 \frontmatter 根本没有定义,以及最要紧的一条建议:别跟学校的模板较劲。

exam 类的骨架——\questionparts 与分值

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 页」是这个类的标准配置,光凭这一点就能认出少了一张纸的答卷。

latex
\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 次,在后者一次都没有。多出来的那一页,正是被隐藏的答案换成的书写空间。

latex
% 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,名字里含 thesisdiss 且提供 .cls 的目录就有 64 个mitthesisucbthesisuiucthesisthuthesisnjuthesisut-thesisyork-thesis……。而那些从未上传到 CTAN 的本地模板数量更为庞大,其中多数以「从学长手里传下来的 .cls」的形式流通。

那么这类模板的内里究竟是什么?绝大多数情况下,就是bookreport 之上罩了一层规定好的封面与页面尺寸的类。所以 \chapter\tableofcontents 在其中都照常工作;反过来说,模板看起来坏掉时,毛病通常出在底座那一层。如果没有硬性规定、要自己选,有分章的论文用 reportbook;想更细致地控制版式,就用 memoir(TeX Live 2024 中为 v3.8.2,日期 2024 年 1 月 26 日,它自称是「configurable book, report, article document class」)或 KOMA-Script 的 scrbook / scrreprt。下一节就从「各自到底定义了什么」来看它们的差别。

reportbook 的区别——单双面,以及章从哪一页开始

区别就写在默认选项里。book.cls\ExecuteOptions{letterpaper,10pt,twoside,onecolumn,final,openright}report.cls 同一行则是 onesideopenany。也就是说,book 按双面排版,每章必定从奇数页(右页)开始,为此有时会插入空白页;report 按单面排版,章直接从下一页开始。若论文要装订成册提交,book 的行为才是对的;若以 PDF 提交、并不预设双面打印,report 更直截了当。article 根本没有 \chapter——需不需要 \chapter,正是选类时的第一个分岔口。

文档类默认单双面章的起始页frontmatter
articleoneside根本没有 \chapter未定义
reportonesideopenany(下一页)未定义
booktwosideopenright(奇数页)已定义
memoir同 book(可配置)openright(可改)已定义
scrbooktwosideopenright已定义
scrreprtonesideopenany未定义

\frontmatter 为何报 ! Undefined control sequence,以及这三个命令实际做了什么

\frontmatter\mainmatter\backmatter 只在 book 系的类里有定义。在 \documentclass{report} 下写 \frontmatter,会得到 ! Undefined control sequence.,并指向 l.3 \frontmatter。检索类文件也证实了这一点:字符串 frontmatter 出现在 book.clsmemoir.clsscrbook.cls 中,而在 article.clsreport.clsscrreprt.cls 中没有。「学校模板里 \frontmatter 不起作用」的求助大多就是这个原因——那份模板的底座是 report。如果不需要章编号方面的行为,手写 \pagenumbering{roman}\pagenumbering{arabic} 就能得到同样的效果,因为这三个命令的内容差不多就只有这些。

那么它们的内容究竟是什么?在 book.cls 里:\frontmatter 执行 \cleardoublepage,置 \@mainmatterfalse,并 \pagenumbering{roman}\mainmatter 执行 \cleardoublepage,置 \@mainmattertrue,并 \pagenumbering{arabic}。而 \backmatter 只是换页并置 \@mainmatterfalse,完全不碰页码。这一点常被误传;凡是说「后置部分会改变页码」的讲解都是错的。\@mainmatter 为假时真正改变的是 \@chapter 的行为:不推进章计数器、标题中不印编号、目录条目不带 \numberline。也就是说,在前置与后置部分,章不带编号,但仍会进目录——致谢和参考文献没有编号却能从目录跳转,论文写作的这一整套惯例,就靠这一个开关实现。

latex
% 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 把带编号的章与不带编号的章分开。能否把一个分支折叠成一个开关,正是在截稿前夜见分晓的地方。