Knuth TeXbook 中的 \answer 命令如何工作

Knuth TeXbook 中的 \answer 命令如何工作

我正在尝试了解\answerDonald Knuth 的 TeXbook 中的命令如何在 LaTeX 中执行相同的操作。请解释其工作原理。是否可以按照这个想法实现练习,以便练习的文本出现两次:在宣布练习的地方和在答案之前?

在 TeXbook 附录 E 第 418 页中写道:

\answer将答案复制到名为的文件中 answers.tex;然后附录 A 通过说“ \immediate\closeout\ans \ninepoint \input answers”来读取此文件。每个单独的答案都以一个空行结尾;因此,\par必须在长答案的段落之间使用。

\newwrite\ans
\immediate\openout\ans=answers % file for answers to exercises
\outer\def\answer{\par\medbreak
  \immediate\write\ans{}
  \immediate\write\ans{\string\ansno\chapno.\the\exno:}
  \copytoblankline}
\def\copytoblankline{\begingroup\setupcopy\copyans}
\def\setupcopy{\def\do##1{\catcode`##1=\other}\dospecials
  \catcode`\|=\other \obeylines}
{\obeylines \gdef\copyans#1
  {\def\next{#1}%
  \ifx\next\empty\let\next=\endgroup %
  \else\immediate\write\ans{\next} \let\next=\copyans\fi\next}}

注意这里的用法\dospecials,设置逐字复制。\ttverbatim宏可以\dospecials以相同的方式调用;但是\ttverbatim使用频率很高,因此为了提高速度进行了简化。

答案1

Knuth 的方法非常面向纯 TeX。假设你真正想要做的是能够在文档中包含,例如,

\begin{question}
What is 6 times 9?
\end{question}

\begin{answer}
42
\end{answer}

...

\chapter{Answers to questions}

\printanswers

并且环境中的所有内容answer仅以 输出\printanswers

我正是通过使用将环境内容保存到文件(加上一些包装器)的方法来做到这一点,answer以便能够生成输出\printanswers。 这是我使用的代码:

\RequirePackage{verbatim} % ❶
\newwrite\ans@out % ❷
\def\answer@chapstart{\immediate\openout\ans@out=\jobname.ans} % ❸
\def\answer{%
  \@bsphack % ❹
  \let\do\@makeother\dospecials % ❺
  \immediate\write\ans@out{\string\preans} % ❻
  \immediate\write\ans@out{\string\par\string\noindent
       {\string\sc\space Exercise\string~\thequestion.}\quad}% ❻
  \immediate\write\ans@out{\string\vadjust{\string\nobreak}\relax} % ❼
  \catcode`\^^M\active % ❺
  \def\verbatim@processline{% ❺
    \immediate\write\ans@out
       {\the\verbatim@line}}%
  \verbatim@start}
\def\endanswer{%
  \immediate\write\ans@out{}% ❽
  \@esphack} % ❹
\def\preans{\if@nobreak\global\@nobreakfalse\else\bigfilbreak\fi} % ❾

\def\printanswers{\immediate\closeout\ans@out % ❿
  \@input{\jobname.ans} % ⓫
}

我将繁重的工作交给了 ❶ 包verbatim,它允许我创建verbatim类似 的环境,这些环境将对其内容进行特殊处理。在这里,我使用了一些从该包的文档中偷来的代码 ❺(有关详细信息,请参阅)来告诉 LaTeX 将和texdoc verbatim之间的所有内容写入外部文件。\begin{answer}\end{answer}

外部文件部分通过 TeX 的写入功能处理。我创建了一个新的写入器 ❷,创建了一个宏来打开文件,该文件被调用作为\chapter❸ 定义的一部分,因为我在每章末尾而不是在书的末尾输出答案¹,但对于你的情况,你可能希望将该行替换为

\immediate\openout\ans@out=\jobname.ans

这样文件就会立即创建。\@bsphack\@esphack❹在这里可能不是必需的,但它本质上告诉 LaTeX,这段代码在调用时不会生成任何输出,因此它在空间上是不可见的。我 20 多年前写了这段代码,所以我不记得当时的动机是什么了。²

我很懒,所以我直接在这里输出格式化答案的代码 ❻。如果我今天这样做,我可能会让它\string\begin{theanswer}{\thequestion}在输出之前和\string\end{theanswer}之后写一些类似的东西。\string在宏中添加前缀是防止在写入时扩展宏的一种原始方法。现在,有更强大的方法来管理这个问题,但同样,你在这里得到的是剪切和粘贴的 20 世纪代码。

思考在 ❼ 处,我确保问题编号标签和随后显示的任何文本之间没有分页符。在 ❽ 处,我在问题末尾放置了一个空行。³

代码\preans❾ 是我从 Knuth 那里偷来的webmac.tex,只有当答案长度超过一页时,它才会跨页。它可以以增加页数为代价让事情看起来更好一些。

最后,当需要打印答案时,关闭❿ \write,然后读取生成的文件⓫。


  1. 让读者翻到这么远才看到答案的解决方案对我来说总感觉有点不友好。
  2. 可能是货物崇拜。
  3. “你必须让篱笆的后面——没有人会看到——看起来和篱笆的前面一样好看。尽管没有人会看到,但你会知道,这将表明你致力于创造完美的东西。” –保罗·乔布斯

相关内容