% -*- coding: utf-8 -*- % This is part of the book TeX for the Impatient. % Copyright (C) 2003 Paul W. Abrahams, Kathryn A. Hargreaves, Karl Berry. % See file fdl.tex for copying conditions. \input macros %\chapter{A compendium \linebreak of useful macros} \chapter{实用宏集概述} \chapterdef{eplain} %This section describes |eplain.tex|, a collection of macros and other %definitions that extend plain \TeX. %\bix^^|eplain.tex| %The descriptions of the various macros explain their purposes, %but usually do not explain how they %work or provide explicit details on %how to use them. That information is contained in the source files for %|eplain.tex| and in the documentation that comes with it. %See \headcit{Resources}{resources} for how to obtain |eplain.tex|. 这一章描述 |eplain.tex|,一组用于扩展 plain \TeX\ 的宏和其他定义。 \bix^^|eplain.tex| 对各种宏的描述解释了它们的目的,但通常不解释它们的工作原理或给出如何使用它们的详情。 那些信息包含在 |eplain.tex| 源码文件以及它的文档中。 在\headcit{资源}{resources}中介绍了获取 |eplain.tex| 的方法。 %\section{Preliminaries} \section{预备定义} %We start with some macros for changing category codes and convenient %definitions for two of the commonly used ones. %^^{category codes//useful definitions for} 我们先介绍几个修改类别码及定义两种常用类别码的宏。 ^^{类别码//有用的类别码定义} %\pix^^|\makeactive| %\pix^^|\letter| %\pix^^|\other| %\pix^^|\uncatcodespecials| \pix^^|\makeactive| \pix^^|\letter| \pix^^|\other| \pix^^|\uncatcodespecials| \csdisplay \def\makeactive#1{\catcode`#1 = \active \ignorespaces}% \chardef\letter = 11 \chardef\other = 12 \def\uncatcodespecials{% \def\do##1{\catcode`##1 = \other}% \dospecials}% Defined in plain. | %In order to define `|^^M|' as an active character, you need to encase %the definition in a group and invoke some extra machinery. %\ttidxref{^^M} %The \pix^|\letreturn| macro lets you define `|^^M|' without that %extra machinery (which you can see in the definition below). 为了将 `|^^M|' 定义为活动字符,你需要将定义放在编组中,并借助某些额外机制。 \ttidxref{^^M} \pix^|\letreturn| 宏让你无需额外机制就可以定义 `|^^M|'(它的定义在下面可以看到)。 \csdisplay {\makeactive\^^M \long\gdef\letreturn#1{\let^^M = #1}}% | %These macros consume one, two, or three arguments. 这些宏用于吸收一个、两个或三个参量。 %\pix^^|\gobble| %\pix^^|\gobbletwo| %\pix^^|\gobblethree| %\csdisplay %\def\gobble#1{}\def\gobbletwo#1#2{}% %\def\gobblethree#1#2#3{}% %| \pix^^|\gobble| \pix^^|\gobbletwo| \pix^^|\gobblethree| \csdisplay \def\gobble#1{}\def\gobbletwo#1#2{}% \def\gobblethree#1#2#3{}% | %Now we establish some conventions for reading the rest of the file. %Within the file we allow ``private'' control sequences that contain %`|@|' in their names. %These control sequences aren't accessible outside of this file (unless %you change the category code of `|@|' again). %\xrdef{eplainconv} 现在我们建立一些惯例,用于读取这个文件的其他部分。 在这个文件中我们允许名称带有 `|@|' 的``私有''控制序列。 这些控制序列在这个文件之外无法使用(除非你再次修改 `|@|' 的类别码)。 \xrdef{eplainconv} \csdisplay \catcode`@ = \letter % Allow control sequences with @. \let\@plainwlog = \wlog % Don't log register allocations. \let\wlog = \gobble \newlinechar = `^^J | %The next two macros provide convenient forms of diagnostic output. %\pix^|\loggingall| turns on all tracing, but causes the trace %output to appear only in the log file and not at your terminal. %\pix^|\tracingboxes| causes boxes to be displayed completely when %they're traced. (\TeX\ normally shows only three levels of boxing %and five items within each box.) 接下来两个宏提供了诊断输出的便捷方式。 \pix^|\loggingall| 打开所有追踪, 但让追踪输出仅写在日志文件中,而不在终端上显示。 \pix^|\tracingboxes| 使得盒子被追踪时其内容完全显示。% (\TeX\ 通常只显示嵌套到三层的盒子,每个盒子显示五个项目。) \csdisplay \def\loggingall{\tracingcommands\tw@\tracingstats\tw@ \tracingpages\@ne\tracingoutput\@ne \tracinglostchars\@ne\tracingmacros\tw@ \tracingparagraphs\@ne\tracingrestores\@ne \showboxbreadth\maxdimen\showboxdepth\maxdimen}% \def\tracingboxes{\showboxbreadth = \maxdimen \showboxdepth = \maxdimen}% | %The default thickness of rules is $0.4$\pt. %You can produce rules of any default thickness you choose by redefining %|\vruledefaultwidth|, |\hruledefaultheight|, and |\hruledefaultdepth| %and then using %\pix^^|\ehrule| %|\eh!-rule| %and \pix^|\evrule| %instead of |\hrule| and |\vrule|. (The `e' stands for %``eplain''.) %^^{rules//thickness of} %If you give an explicit %dimension (e.g., |\ehrule height 16pt|), \TeX\ will use it. 标线的默认厚度为 $0.4$\pt 。 要生成默认厚度为任何值的标线, 你可以重新定义 |\vruledefaultwidth|、|\hruledefaultheight| 及 |\hrule!-defaultdepth|, 并将 |\hrule| 和 |\vrule| 换为 \pix^|\ehrule| 和 \pix^|\evrule|。% (名称中的 `e' 代表 ``eplain''。)% ^^{标线//标线的厚度} 如果你显式给出尺寸(比如 |\ehrule height 16pt|),\TeX\ 将使用它。 \csdisplay \newdimen\hruledefaultheight \hruledefaultheight = 0.4pt \newdimen\hruledefaultdepth \hruledefaultdepth = 0.0pt \newdimen\vruledefaultwidth \vruledefaultwidth = 0.4pt \def\ehrule{\hrule height\hruledefaultheight depth\hruledefaultdepth}% \def\evrule{\vrule width\vruledefaultwidth}% | %The |\%| convention for writing a `|%|' character doesn't work when you %want to include that character in the token list of |\write|. %^^|\write//writing \b\tt\%\e\ with| %^^|%//writing out| %You can use ^|\percentchar| to achieve this. %We also redefine ^|^^L| to be nonouter so that you can use it in a %macro definition or argument. |\%| 通常用于写出 `|%|' 字符,但它无法包含在 |\write| 的记号列中。 ^^|\write//用它写出 \b\tt\%\e\| %^^|%//写出该字符| 你可以用 ^|\percentchar| 达到此目的。 我们还将 ^|^^L| 重新定义为非外部宏,因此你可以在宏定义或者参量中使用它。 \csdisplay {\catcode`\% = \other \gdef\percentchar{%}}% \def^^L{\par }% | %\pix^|\tokstostring| converts its argument into a list of ^{character %tokens}. %It uses only expansions that are handled in \TeX's gullet. %This property is necessary %for it to work with |\edef|. It is used by the cross-referencing %macros (\xref{xrefs}). \pix^|\tokstostring| 将它的参量转换为一列^{字符记号}。 它仅用到在 \TeX\ 的食道中处理的展开。 此特性是和 |\edef| 一起使用时所必需的。它用于宏的交叉引用(\xref{xrefs})。 %In order to split the argument up at spaces, we have to use two %subsidiary macros. |\@ttsA| finds the spaces, and |\@ttsB| handles a %token sequence without any spaces. Each space is replaced by the %expansion of \pix^|\spacesub|. 为了将参量在空格处分开,我们要用到两个子宏。 |\@ttsA| 用于寻找空格,而 |\@ttsB| 用于处理不带空格的控制序列。 每个空格都被替换为 \pix^|\spacesub| 的展开。 \csdisplay \def\tokstostring#1{\@ttsA#1 \ttsmarkA}% \def\@ttsA#1 #2\ttsmarkA{\ifempty{#1}\else \@ttsB #1\@ttsmarkB \ifempty{#2}\else \spacesub\@ttsA#2\ttsmarkA\fi\fi}% \def\@ttsB#1{\ifx #1\@ttsmarkB\else \string #1% \expandafter\@ttsB\fi}% \def\@ttsmarkB{\@ttsmarkB}% should never be expanded \def\spacesub{+}% | %\noindent %\pix^|\ifempty| tests if its argument is empty. \noindent \pix^|\ifempty| 检测它的参量是否为空。 \csdisplay \def\ifempty#1{\@ifempty #1\@emptymarkA\@emptymarkB}% \def\@ifempty#1#2\@emptymarkB{\ifx #1\@emptymarkA}% \def\@emptymarkA{\@emptymarkA}% | %The ^|\for| macro implements a \TeX\ version of the ^{for loop} in %traditional programming languages. These macros come directly from %\LaTeX. ^|\for| 宏实现了其他传统编程语言中的^{for循环}的 \TeX\ 版本。 这些宏直接取自 \LaTeX 。 \csdisplay \def\for#1:=#2\do#3{\edef\@fortmp{#2}% \ifx\@fortmp\empty \else \expandafter\@forloop#2,\@nil,\@nil\@@#1{#3}\fi}% \def\@nnil{\@nil}% \def\@fornoop#1\@@#2#3{}% \def\@forloop#1,#2,#3\@@#4#5{\def#4{#1}\ifx #4\@nnil \else #5\def#4{#2} ifx #4\@nnil \else #5\@iforloop #3\@@#4{#5}\fi\fi}% \def\@iforloop#1,#2\@@#3#4{\def#3{#1}\ifx #3\@nnil \let\@nextwhile=\@fornoop \else #4\relax \let\@nextwhile=\@iforloop\fi \@nextwhile#2\@@#3{#4}}% | %\pix^|\obeywhitespace| is useful for reproducing line breaks, blank lines, %and spaces in your input. It combines the effects of |\obey!-lines| and %|\obey!-spaces|, %^^|\obeylines| ^^|\obeyspaces| %and also causes spaces at the start of a line to %be printed. Tab characters are not affected by this; they still produce %normal glue. %\xrdef{ewhitesp} %^^{space characters} %^^|\fontdimen| %^^|\font| %^^|\letreturn| \pix^|\obeywhitespace| 用于重新生成你的输入中的换行、空行和空格。 它结合了|\obey!-lines| 和 |\obey!-spaces| 的效果, ^^|\obeylines| ^^|\obeyspaces| 并把行首的空格也打印出来。制表符不受它影响;它们仍然生成正常的粘连。 \xrdef{ewhitesp} ^^{空格符} ^^|\fontdimen| ^^|\font| ^^|\letreturn| \csdisplay \def\alwaysspace{\hglue\fontdimen2\the\font \relax}% {\makeactive\^^M \makeactive\ % \gdef\obeywhitespace{% \makeactive\^^M\def^^M{\par\indent}% \aftergroup\@removebox% Kill extra paragraph at end. \makeactive\ \let =\alwaysspace}}% \def\@removebox{\setbox0=\lastbox} | %\pix^|\frac| is a good way to print fractions in text when you don't %want to use |\over| and %``1/2'' just doesn't look right. This macro is the answer to %\knuth{Exercise~11.6}.^^{fractions//slashed form} 当你不想用 |\over|,而 ``1/2'' 看来又不够好时, 你最好用 \pix^|\frac| 在正文中打印分式。 这个宏是\knuth{练习~11.6}的答案。^^{分式//斜线形式} \csdisplay \def\frac#1/#2{\leavevmode \kern.1em \raise .5ex \hbox{\the\scriptfont0 #1}% \kern-.1em $/$% \kern-.15em \lower .25ex \hbox{\the\scriptfont0 #2}}% | %The following macros produce logos that are useful in the \TeX\ world. %The \AMSTeX\ logo is from \knuth{page~420}. The \LaTeX\ logo is %slightly modified from the one in |latex.tex| (we use a different font %for the `A'); similarly, the \BibTeX\ logo uses |\sevenrm| instead of a %true caps-and-small-caps font. The |.mf| source file for the \MF\ logo %is given in the \MF\ manual: %\smallskip %{\narrower\noindent %^{Knuth, Donald E.},{\sl The {\logosl METAFONT}book}. % Reading, Mass.: Addison-Wesley, 1986.\par} %\smallskip %\pix^^|\LaTeX| %\pix^^|\AMSTeX| %\pix^^|\BibTeX| %\pix^^|\MF| 下面这些宏生成 \TeX\ 圈中常用的标识。 \AMSTeX\ 标识取自 \knuth{第~420~页}。 \LaTeX\ 标识取自 |latex.tex| 并稍作修改(我们修改了 `A' 的字体); 类似地,\BibTeX\ 标识用 |\sevenrm| 字体代替真正的小型大写字体。 \MF\ 标识的 |.mf| 源文件出现在 \MF\ 手册中: \smallskip {\narrower\noindent ^{Knuth, Donald E.},{\sl The {\logosl METAFONT}book}. Reading, Mass.: Addison-Wesley, 1986.\par} \smallskip \pix^^|\LaTeX| \pix^^|\AMSTeX| \pix^^|\BibTeX| \pix^^|\MF| \csdisplay \def\LaTeX{L\kern-.26em \raise.6ex\hbox{\fiverm A}% \kern-.15em TeX}% \def\AMSTeX{$\cal A\kern-.1667em \lower.5ex\hbox{$\cal M$}% \kern-.125em S$-\TeX}% \def\BibTeX{{\rm B\kern-.05em {\sevenrm I\kern-.025em B}% \kern-.08em T\kern-.1667em \lower.7ex\hbox{E}% \kern-.125emX}}% \font\mflogo = logo10 \def\MF{{\mflogo META}{\tenrm \-}{\mflogo FONT}}% | %The next two macros produce boxes. \pix^|\blackbox| produces a ``square %bullet'', used in the list macros (\xref{listmacs}). %\pix^|\makeblankbox| (from \knuth{page~311}) produces an unfilled %rectangle, with the thickness of the border rules given by the %arguments. 接下来的两个宏用于生成盒子。 \pix^|\blackbox| 生成一个方块,它用在列表宏中(\xref{listmacs})。 \pix^|\makeblankbox|(取自\knuth{第~311~页})生成一个空心矩形, 它的边框厚度由参量指定。 \csdisplay \def\blackbox{\vrule height .8ex width .6ex depth -.2ex}% \def\makeblankbox#1#2{% \hbox{\lower\dp0\vbox{\hidehrule{#1}{#2}% \kern -#1% overlap rules \hbox to \wd0{\hidevrule{#1}{#2}% \raise\ht0\vbox to #1{}% vrule height \lower\dp0\vtop to #1{}% vrule depth \hfil\hidevrule{#2}{#1}}% \kern-#1\hidehrule{#2}{#1}}}}% \def\hidehrule#1#2{\kern-#1\hrule height#1 depth#2 \kern-#2}% \def\hidevrule#1#2{\kern-#1{\dimen0 = #1 \advance\dimen0 by #2 \vrule width\dimen0}\kern-#2}% | %\pix^|\numbername| produces the written-out form of a number. (If the %number is greater than ten, the macro just reproduces the numerals %of its argument.) \pix^|\numbername| 从数字生成其书面名称% (若数字大于十,这个宏仅重新生成参量的数值。) \csdisplay \def\numbername#1{\ifcase#1% zero\or one\or two\or three\or four\or five% \or six\or seven\or eight\or nine\or ten\or #1\fi}% | %\pix^|\testfileexistence| determines whether a file |\jobname.#1| is %non\-empty and sets |\if!-fileexists| appropriately. %^^{files//testing for existence of} %The file name in the argument need not end in a space token since %the macro provides the space token. \pix^|\testfileexistence| 判定文件 |\jobname.#1| 是否非空, 并相应地设定 |\if!-fileexists| 的值。 ^^{文件//检测文件是否存在} 参量中的文件名无需以空格记号结尾,因为这个宏已经提供了空格记号。 \csdisplay \newif\iffileexists \def\testfileexistence#1{\begingroup \immediate\openin0 = \jobname.#1\space \ifeof 0\global\fileexistsfalse \else \global\fileexiststrue\fi \immediate\closein0 \endgroup}% | %\section Displays \section 陈列公式 %By default, \TeX\ centers displayed material (the material between %|$$|'s). ^^{displays, formatting} \pix^|\leftdisplays| causes displays %to be left-justified by default. You can return to centered displays %with \pix^|\centereddisplays|. \TeX\ 默认将陈列公式(放在 |$$| 之间的公式)居中显示。 ^^{陈列公式//显示形式}\pix^|\leftdisplays| 让陈列公式默认为左对齐显示。 你也可以用 \pix^|\centereddisplays| 命令恢复居中显示。 %The macros here are more general than they need to be just for doing %left-justified displays. For every display, %\pix^|\ifeqno| will be true if an |\eqno| occurred in the display. %\pix^|\ifleqno| will be true if an |\leqno| occurred. If either %kind of equation number occurred, \pix^|\eqn| produces the text of the %equation number. \pix^|\eq| always produces the text of the %equation itself. 这些宏提供让陈列公式左对齐显示之外的更多功能。 对每个陈列公式,若 |\eqno| 存在则 \pix^|\ifeqno| 为真, 若 |\leqno| 存在则 \pix^|\ifleqno| 为真。 如果其中一种公式编号存在,\pix^|\eqn| 将排印公式编号的内容。 而 \pix^|\eq| 始终排印公式本身的内容。 %These macros are based on the code on \knuth{page~376}. 这些宏基于\knuth{第~376~页}中的代码。 \csdisplay \newif\ifeqno \newif\ifleqno \newtoks\@eqtoks \newtoks\@eqnotoks \def\eq{\the\@eqtoks}\def\eqn{\the\@eqnotoks}% \def\displaysetup#1$${% \@displaytest#1\eqno\eqno\@displaytest}% \def\@displaytest#1\eqno#2\eqno#3\@displaytest{% \if !#3!% No \eqno, check for \leqno: \@ldisplaytest#1\leqno\leqno\@ldisplaytest \else \eqnotrue \leqnofalse % Have \eqno, not \leqno. \@eqnotoks = {#2}\@eqtoks = {#1}% \fi \generaldisplay$$}% \def\@ldisplaytest#1\leqno#2\leqno#3\@ldisplaytest{% \@eqtoks = {#1}% \if !#3!% \eqnofalse % No \leqno; we're done. \else \eqnotrue \leqnotrue % Have \leqno. \@eqnotoks = {#2}% \fi}% | %You can format displays differently by defining your own macro, %analogous to \pix^|\leftdisplays|. %The macro definition must %place a call on |\display!-setup| in |\every!-display| ^^|\everydisplay| %so as to ensure %that |\dis!-play!-setup| ^^|\displaysetup| is called at the start of %every display. The macro definition must also include a definition of %^|\generaldisplay|. 通过自己定义类似 \pix^|\leftdisplays| 的宏,你可以给陈列公式设定不同的形式。 这个宏定义中必须在 |\every!-display| ^^|\everydisplay|中调用 |\display!-setup|, 以确保 |\dis!-play!-setup| ^^|\displaysetup|在每个陈列公式开始都被调用。 这个宏定义必须也包含 ^|\generaldisplay| 的定义。 \csdisplay \newtoks\previouseverydisplay \def\leftdisplays{% \previouseverydisplay = \everydisplay \everydisplay = {\the\previouseverydisplay \displaysetup}% \def\generaldisplay{% \leftline{% \strut \indent \hskip\leftskip \dimen0 = \parindent \advance\dimen0 by \leftskip \advance\displaywidth by -\dimen0 \@redefinealignmentdisplays \ifeqno \ifleqno \kern-\dimen0 \rlap{$\displaystyle\eqn$}% \kern\dimen0 \fi\fi $\displaystyle{\eq}$% \ifeqno \ifleqno\else \hfill $\displaystyle{\eqn}$% \fi\fi}}}% \def\centereddisplays{\let\displaysetup = \relax}% | %\filbreak %\pix^|\leftdisplays| must go to some pains to make sure that %|\dis!-play!-lines|, ^^|\displaylines| %^|\eqalignno|, and ^|\leqalignno| still work properly. %|\eq| is typeset in math mode, %and |\halign| is illegal in math mode. %^^|\halign//illegal in math mode| %We use ^|\vcenter| to change the context so that |\halign| becomes %legal again. We also remove the |\hfil| commands at the left of the template %to obtain the flush left formatting. Other than those changes, the macros %are the same as in |plain.tex|. %\filbreak \pix^|\leftdisplays| 必须历经辛苦地确保 |\dis!-play!-lines|^^|\displaylines|、 ^|\eqalignno| 和 ^|\leqalignno| 还能正常使用。 |\eq| 在数学模式中排版,而 |\halign| 在数学模式中是不合法的。 ^^|\halign//在数学模式中不合法| 我们用 ^|\vcenter| 改变环境,以让 |\halign| 重新变成合法的。 我们还去掉模板左边的 |\hfil| 命令以得到左对齐格式。 除这些改变之外,这些宏与 |plain.tex| 中的相同。 \csdisplay \def\@redefinealignmentdisplays{% \def\displaylines##1{\displ@y \vcenter{\halign{\hbox to\displaywidth{$\@lign \displaystyle####\hfil$}\crcr##1\crcr}}}% \def\eqalignno##1{\displ@y \vcenter{\halign to\displaywidth{% $\@lign\displaystyle{####}$\tabskip\z@skip &$\@lign\displaystyle{{}####}$ \hfil\tabskip\centering &\llap{$\@lign####$}\tabskip\z@skip\crcr ##1\crcr}}}% \def\leqalignno##1{\displ@y \vcenter{\halign to\displaywidth{% $\@lign\displaystyle{####}$\tabskip\z@skip &$\@lign\displaystyle{{}####} $\hfil\tabskip\centering &\kern-\displaywidth \rlap{\kern-\parindent\kern-\leftskip$ \@lign####$}% \tabskip\displaywidth\crcr ##1\crcr}}}}% | %\section Time of day \section 日期时间 %When \TeX\ starts up, it sets the values of the %^|\time|, ^|\day|, ^|\month|, and ^|\year| parameters. %^^{time of day}^^{date} %\pix^|\monthname| %produces the name of the month, abbreviated to three letters. %\pix^|\timestring| produces the current time, as in ``1:14\thinspace %p.m.''. \pix^|\timestamp| produces the text of the complete date, as %in ``23 Apr 1964\quad 1:14\thinspace p.m.''. \TeX\ 开始运行时设定了 ^|\time|、^|\day|、^|\month| 和 ^|\year| 参数的值。 ^^{日期时间}^^{日期} \pix^|\monthname| 排印月份名称的三字母缩写。 \pix^|\timestring| 排印类似当前时间,类似于 ``1:14\thinspace p.m.''。 \pix^|\timestamp| 排印完整日期文本,类似于 ``23 Apr 1964\quad 1:14\thinspace p.m.''。 \csdisplay \def\monthname{% \ifcase\month \or Jan\or Feb\or Mar\or Apr\or May\or Jun% \or Jul\or Aug\or Sep\or Oct\or Nov\or Dec% \fi}% \def\timestring{\begingroup \count0 = \time \divide\count0 by 60 \count2 = \count0 % The hour. \count4 = \time \multiply\count0 by 60 \advance\count4 by -\count0 % The minute. \ifnum\count4<10 \toks1 = {0}% Get a leading zero. \else \toks1 = {}% \fi \ifnum\count2<12 \toks0 = {a.m.}% \else \toks0 = {p.m.}% \advance\count2 by -12 \fi \ifnum\count2=0 \count2 = 12 \fi % Make midnight `12'. \number\count2:\the\toks1 \number\count4 \thinspace \the\toks0 \endgroup}% \def\timestamp{\number\day\space\monthname\space \number\year\quad\timestring}% | %\section Lists \section 列表 %\null %\xrdef{listmacs} %\bix^^{itemized lists} %\bix^^{enumerations} %\pix^|\numberedlist| produces numbered lists; |\endnumberedlist| ends %them. %\pix^|\unorderedlist| is analogous. %For either of these, items inside the lists begin %with \pix^|\li| (``list item''). You can put \pix^|\listcompact| %at the beginning of a list if %you don't want any additional space between the items of that list. %Lists can be nested arbitrarily. \null \xrdef{listmacs} \bix^^{编号列表} \bix^^{列举} \pix^|\numberedlist| 排印编号列表;|\endnumberedlist| 结束该列表。 类似地,\pix^|\unorderedlist| 排印无序列表。 这两种列表中的项目以 \pix^|\li|(``list item'')开头。 如果不需要列表项目间的额外间隔,你可以在列表开始处加上 \pix^|\listcompact|。 列表可以任意嵌套。 %You can control the spacing between the items more generally by %assigning values to the registers listed below. If the items in %your lists tend to be long, you might want to make |\interitemskip| %nonzero. %The left indentation of each list item is given by |\par!-indent| plus %|\list!-left!-indent|; the right indentation of each list item %is given by |\list!-right!-indent|. 更一般地,你可以通过改变下列寄存器的值控制列表项目间的间隔。 如果列表项目经常比较长,你也许希望设定非零的 |\interitemskip|。 各列表项目的左缩进量等于 |\par!-indent| 加 |\list!-left!-indent|; 各列表项目的右缩进量等于 |\list!-right!-indent|。 \csdisplay \newskip\abovelistskip \abovelistskip = .5\baselineskip \newskip\interitemskip \interitemskip = 0pt \newskip\belowlistskip \belowlistskip = .5\baselineskip \newdimen\listleftindent \listleftindent = \parindent \newdimen\listrightindent \listrightindent = 0pt \def\listcompact{\interitemskip = 0pt \relax}% | %Both numbered and unnumbered lists use the macros that follow. %We don't change |\parindent|, since many existing macros, e.g., %|\footnote|, depend on |\parindent|. %We must account for the %possibility that items are more than one paragraph long. In this case, all %paragraphs after the first will be indented. %We use %|\leftskip| and |\rightskip| to indent the list items. %Indentation of displays is accounted for by changes to |\every!-dis!-play|. %^^|\everydisplay| 编号列表和无序列表都使用下面这些宏。 我们并不改变 |\parindent| 的值, 因为很多现有的宏比如 |\footnote| 依赖于 |\parindent|。 我们必须考虑到项目包含不止一个段落的可能性。 在这种情形中,除第一个之外的所有段落都将被缩进。 我们用 |\leftskip| 和 |\rightskip| 缩进列表项目。 我们通过改变 |\every!-dis!-play| 设定陈列公式的缩进。 ^^|\everydisplay| \csdisplay \newdimen\@listindent \def\beginlist{% \@listindent = \parindent \advance\@listindent by \listleftindent \everydisplay = \expandafter{\the\everydisplay % Don't lose user's \everydisplay: \advance\displayindent by \@listindent \advance\displaywidth by -\@listindent \advance\displaywidth by -\listrightindent}% \nobreak\vskip\abovelistskip \parskip = 0pt % \leftskip shifts nested lists to the right on the page. \advance\leftskip by \@listindent \advance\rightskip by \listrightindent}% \def\printitem{\par\noindent \llap{\hskip-\listleftindent \marker \enspace}}% \def\endlist{\vskip\belowlistskip}% | %\noindent %You can change the way the item labels are typeset by %redefining the |\numbered!-marker| macro. %\pix^^|\numberedmarker| \noindent 通常重新定义 |\numbered!-marker| 宏,你可以改变项目标签的样式。 \pix^^|\numberedmarker| \csdisplay \newcount\numberedlistdepth \newcount\itemnumber \newcount\itemletter \def\numberedmarker{% \ifcase\numberedlistdepth (impossible)% \or \itemnumberout)% \or \itemletterout)% \else *% \fi}% | %\noindent Here are the definitions of |\numberedlist| and %|\unorderedlist|. %Both definitions have the same structure. \noindent 这里是 |\numberedlist| 和 |\unorderedlist| 的定义。 这两个定义的结构是相同的。 \csdisplay \def\numberedlist{\environment{@numbered-list}% \advance\numberedlistdepth by 1 \itemnumber = 1 \itemletter = `a \beginlist \let\marker = \numberedmarker \def\li{% \ifnum\itemnumber=1\else \vskip\interitemskip \fi \printitem \advance\itemnumber by 1 \advance\itemletter by 1 }}% \def\itemnumberout{\number\itemnumber}% \def\itemletterout{\char\itemletter}% \def\endnumberedlist{\par \endenvironment{@numbered-list}\endlist}% !bigskip \newcount\unorderedlistdepth \def\unorderedmarker{% \ifcase\unorderedlistdepth (impossible)% \or \blackbox \or ---% \else *% \fi}% \def\unorderedlist{\environment{@unordered-list}% \advance\unorderedlistdepth by 1 \beginlist \itemnumber = 1 \let\marker = \unorderedmarker \def\li{% \ifnum\itemnumber=1\else \vskip\interitemskip \fi \printitem \advance\itemnumber by 1 }}% \def\endunorderedlist{\par \endenvironment{@unordered-list}\endlist}% | %\eix^^{itemized lists} %\eix^^{enumerations} \eix^^{编号列表} \eix^^{列举} %\section Verbatim listing \section 原文呈现 %The \pix^|\listing| macro produces a verbatim listing of a specified %file in the |\tt| font. %It is based on the code on \knuth{page~380}. %Tabs produce a fixed amount of space, and form %feeds produce a page break. Other control characters produce whatever %happens to be at that font position, which is generally not very useful. %By redefining |\setup!-listing!-hook|, %\pix^^|\setuplistinghook| %you can take additional actions that are appropriate for your particular %fonts and\slash or environment before the file is read in. \pix^|\listing| 宏用 |\tt| 字体排印指定文件的原文呈现。 它基于\knuth{第~380~页}中的代码。 制表符生成固定大小的间隔,而换页符生成一个分页。 其他控制字符生成该字体所在位置的字符,这通常不太有用。 通过重新定义 |\setup!-listing!-hook|\pix^^|\setuplistinghook|, 你可以在读入文件前针对特别字体和\slash 或使用环境作额外定制。 \csdisplay \def\listing#1{% \par \begingroup \@setuplisting \setuplistinghook \input #1 \endgroup}% \let\setuplistinghook = \empty \def\@setuplisting{% \uncatcodespecials \obeywhitespace \makeactive\` \makeactive\^^I \def^^L{\vfill\eject}\tt}% {\makeactive\` \gdef`{\relax\lq}}% Defeat ligatures. {\makeactive\^^I\gdef^^I{\hskip8\fontdimen2\tt \relax}}% | %\section Tables of contents \section 目录 %\null ^^{table of contents} %The macro \pix^|\writetocentry| writes a macro call to the file %|\jobname.toc|. The first argument of |\writetocentry|, e.g., %``chapter'', is used to compose the name of the called macro. The second %argument is the text to appear in the table of contents entry. %|\writetocentry| appends the page number to the macro call. For %example: \null ^^{目录} \pix^|\writetocentry| 宏在 |\jobname.toc| 文件中写入一个宏调用。 |\write!-tocentry| 的第一个参量,比如``chapter'',用于构成所调用的宏的名称。 第二个参量是显示在目录项中的文本。 |\writetocentry| 添加页码到宏调用中。例如: \csdisplay \writetocentry{chapter}{Introduction} | %\noindent %will produce the line: \noindent 将生成下列这行: \csdisplay \tocchapterentry{Introduction}{2} | %\noindent in the |.toc| file, indicating %that `Introduction' started on page 2. \noindent 到 |.toc| 文件中,表示 `Introduction' 从第2页开始。 %You can use |\writenumberedtocentry| to provide a third parameter, such %as a chapter number. For example: 利用 |\writenumberedtocentry| 你还可以提供第三个参数,比如章编号。例如: \csdisplay \writenumberedtocentry{chapter}{The second chapter}{2} | %\noindent will write a line: \noindent 将写入这一行: \csdisplay \tocchapterentry{The second chapter}{2}{14} | %\noindent %You can also |\write| to |\tocfile| yourself. \noindent 你也可以自己用 |\write| 命令写入 |\tocfile|。% \footnote{译注:原文的代码中遗漏 |\writetocentry| 的定义, 而将 |\writenumberedtocentry| 的定义重复写了两遍,译文已修正此问题。 实际上,在本书所用的 |eplain.tex| 1.9 中, |\writetocentry| 调用 |\writenumberedtocentry|, 并在后者中根据第三个参数是否非空写入不同的目录项。 但在 |\writenumberedtocentry| 的定义中有问题, 导致写入 |tocfile| 文件的目录项始终有三个参数。 在翻译时译者也同时修正 |eplain.tex| 中的这个错误。} \csdisplay \newwrite\tocfile \newif\iftocfileopened \def\opentocfile{\iftocfileopened\else \tocfileopenedtrue \immediate\openout\tocfile = \jobname.toc \fi}% \def\writetocentry#1#2{\ifrewritetocfile \opentocfile \write\tocfile{% \expandafter\noexpand \csname toc#1entry\endcsname {#2}{\folio}}% \ignorespaces\fi}% \def\writenumberedtocentry#1#2#3{\ifrewritetocfile \opentocfile \write\tocfile{% \expandafter\noexpand \csname toc#1entry\endcsname {#2}{#3}{\folio}}% \ignorespaces\fi}% | %To produce a table of contents, read the |.toc| file with %\pix^|\readtocfile|. You should call |\read!-tocfile| before the first %|\write!-toc!-entry|. When you're processing the table of contents %without regenerating it, you should not rewrite %the |.toc| file---if you do, its contents will be lost. %The command %|\rewrite!-tocfile!-false| will prevent the rewrite. 要排印出目录,只需用 \pix^|\readtocfile| 读取 |.toc| 文件。 你应该在首次使用 |\write!-toc!-entry| 之前调用 |\read!-tocfile|。 在处理目录且不想重新生成它时, 务必不要改写 |.toc| 文件——如果你这样做,文件内容将会丢失。 命令 |\rewrite!-tocfile!-false| 将禁止这种改写。 \csdisplay \newif\ifrewritetocfile \rewritetocfiletrue \def\readtocfile{\testfileexistence{toc}% \iffileexists \input \jobname.toc \ifrewritetocfile \opentocfile \fi \fi}% | %Here are some definitions of possible |\toc|\dots|entry| macros. These %definitions are meant only as examples---running leaders across the line %is usually not the best way to typeset a table of contents. 这里给出可能出现的 |\toc|\dots|entry| 宏的一些定义。 这些定义只是作为例子而已——在目录中使用指引线通常不是最好的做法。 \csdisplay \def\tocchapterentry#1#2{\line{\bf #1 \dotfill\ #2}}% \def\tocsectionentry#1#2{% \line{\quad\sl #1 \dotfill\ \rm #2}}% \def\tocsubsectionentry#1#2{% \line{\qquad\rm #1 \dotfill\ #2}}% | %\section Cross-references \section 交叉引用 %\null ^^{cross-references} %\xrdef{xrefs} %The macros that follow provide symbolic %cross-referencing, so that you can refer to something in another part of %a document by name instead of by its actual page number. %\pix^|\xrdef||{foo}| defines a label |foo| to be the current page %number, and \pix^|\xrefn||{foo}| produces that page number, e.g., $77$. %More often you'll want to say something like ``see p.\thinspace77'', so %\pix^|\xref||{foo}| produces ``p.\thinspace 77''. If |foo| is not %defined, a warning message will be given. |\xrefwarningfalse| %suppresses the warning. \null ^^{交叉引用} \xrdef{xrefs} 接下来的宏提供了符号式的交叉引用, 让你可以在文档中通过名称而非实际页码提及其它部分的东西。 \pix^|\xrdef||{foo}| 定义标签 |foo| 为当前页码, 而 \pix^|\xrefn||{foo}| 生成该页码,比如 $77$。 你更常用的是类似 ``see p.\thinspace77'' 的写法, 因此 \pix^|\xref||{foo}| 生成 ``p.\thinspace 77''。 如果 |foo| 未定义,就会得到一个警告信息。 |\xrefwarningfalse| 取消这种警告。 %These macros provide no protection against duplicate definitions. You can %check for duplicate definitions by sorting the cross-reference file and %checking, either mechanically or by eye, for adjacent definitions of %the same symbol. 这些宏没有提供对重复定义的保护。要检查重复定义, 你可以将交叉引用文件排序,并用工具或肉眼检查同一个符号的相邻定义。 \csdisplay \newif\ifxrefwarning \xrefwarningtrue \def\xrdef#1{\begingroup \xrlabel{#1}% \edef\@wr{\@writexrdef{\the\@xrlabeltoks}}% \@wr \endgroup \ignorespaces}% \def\@writexrdef#1{\write\reffile{% \string\gdef \expandafter\string\csname#1\endcsname {\noexpand\folio}\percentchar}}% \def\xrefnumber#1{% \xrlabel{#1}% % \@xrlabeltoks now has the control sequence name. \toks0 = \expandafter{\csname\the\@xrlabeltoks\endcsname}% \expandafter \ifx\the\toks0\relax \ifxrefwarning \message{Undefined label `\tokstostring{#1}'.}\fi {\let\spacesub = \space \expandafter\xdef\the\toks0 {`{\tt \tokstostring{#1}}'}}\fi \the\toks0}% \def\xref#1{p.\thinspace\xrefnumber{#1}}% \def\xrefn#1{\xrefnumber{#1}}% | %This macro turns a label into a list of character tokens in the token %register |\labeltoks|. A label can include blanks and control sequences in %it as well as normal characters, but it can't include braces. 这个宏将一个标签转换为一列字符记号,并放在寄存器 |\labeltoks| 中。 除了普通字符之外,标签还可以包含空格和控制序列,但不能包含花括号。 \csdisplay \newtoks\@xrlabeltoks \def\xrlabel#1{\begingroup \escapechar = `\_ \edef\tts{\tokstostring{#1_}}% \global\@xrlabeltoks = \expandafter{\tts}% \endgroup}% | %It takes two passes to get the cross-references right, since the %definitions are written out to the auxiliary file |\jobname.aux|. %\pix^|\readreffile| reads them back in. If you don't issue this command %before the first definition, you'll lose all the definitions from the %previous run. 需要运行两遍才能得到正确的交叉引用, 因为这些定义是写出到辅助文件|\jobname.aux| 里的。 \pix^|\readreffile| 命令用于将它们读取回来。 如果你不在第一个定义之前执行此命令,你将丢失上一遍运行得到的定义。 \csdisplay \newwrite\reffile \newif\ifreffileopened \def\openreffile{\ifreffileopened\else \reffileopenedtrue \immediate\openout\reffile = \jobname.aux \fi}% \def\readreffile{% \testfileexistence{aux}% \iffileexists \begingroup \@setletters \input \jobname.aux \endgroup \else \message{No cross-reference file; I won't give you warnings about undefined labels.}% \xrefwarningfalse \fi \openreffile}% \def\@setletters{% \catcode`_ = \letter \catcode`+ = \letter \catcode`- = \letter \catcode`@ = \letter \catcode`0 = \letter \catcode`1 = \letter \catcode`2 = \letter \catcode`3 = \letter \catcode`4 = \letter \catcode`5 = \letter \catcode`6 = \letter \catcode`7 = \letter \catcode`8 = \letter \catcode`9 = \letter \catcode`( = \letter \catcode`) = \letter}% | %^^{equations, labelling} %You can give symbolic names to equations in a similar way, using %\pix^|\eqdef| and \pix^|\eqref|. |\eqdef| inserts its own |\eqno| %command, so it must be invoked in a place where |\eqno| is legal. ^^{公式编号} 按同样的方式,你可以用 \pix^|\eqdef| 和 \pix^|\eqref| 给出公式的符号式名称。 |\eqdef| 插入自己的 |\eqno| 命令,因此它必须用在能用 |\eqno| 的地方。 \csdisplay \newcount\eqnumber \def\eqdef#1{\global\advance\eqnumber by 1 \expandafter\xdef \csname#1eqref\endcsname{\the\eqnumber}% \immediate\write\reffile{\string\def \expandafter\string\csname#1eqref\endcsname {\the\eqnumber}}% \eqno \eqprint{\the\eqnumber}}% | %|\eqref| produces ``(equation number)''. You can handle fancier %formatting by redefining \pix^|\eqprint|. For example, you could redefine %it so that the equation numbers include %the chapter number. |\eqref| 生成``(公式编号)''。 通过重新定义 \pix^|\eqprint|,你可以实现更加复杂的格式。 举个例子,你可以重新定义它,使得公式编号中包含章编号。 \csdisplay \def\eqref#1{% \expandafter \ifx \csname#1eqref\endcsname \relax \ifxrefwarning \message{Undefined equation label `#1'.}\fi \expandafter\def\csname#1eqref\endcsname{00}% \else \eqprint{\csname#1eqref\endcsname}% \fi}% \def\eqprint#1{(#1)}% | %\section Environments \section 环境 %\null ^^{environments} These macros let you define your own named groups %(environments) for parts of your manuscript. Like \TeX\ groups, these %groups can be nested, and in fact their nesting can be intertwined with %the nesting of \TeX\ groups. If the names at the beginning and end of %an environment don't match, you'll get an error message. The macros are %designed so that the message you get when such an error occurs will give %you a good chance of localizing the cause of the error easily. \null ^^{环境}这些宏让你可以将手稿的一部分定义为命名编组(环境)。 类似 \TeX\ 的编组,这些编组也可以嵌套, 而且实际上它们的嵌套可以与 \TeX\ 编组的嵌套相互交织。 如果开始的名称和结束的名称不匹配,你将得到一个错误信息。 这些宏如此设计,使得当你得到这种错误信息时,你有机会方便地定位错误的来源。 %You begin an environment with %\pix^^|\environment| %|\envi!-ron!-ment| |{foo}| and end it with |\endenvironment{foo}|, where %|foo| is the name of the environment. Our macros slightly improve on %the answer to Exercise~5.7 of \texbook, by doing some checks on %|\begingroup| and |\endgroup| pairs, as well as making sure %|\environment| and |\endenvironment| pairs match. 用 \pix^^|\environment||\envi!-ron!-ment| |{foo}| 开始一个环境, 用 |\endenvironment{foo}| 结束该环境,其中 |foo| 是该环境的名称。 我们的宏稍微改进了 \texbook\ 练习~5.7, 添加对 |\begingroup| 和 |\endgroup| 是否配对的检查, 并确保 |\environment| 和 |\endenvironment| 能够匹配。 \csdisplay \def\environment#1{\ifx\@groupname\undefined\else \errhelp = \@unnamedendgrouphelp \errmessage{`\@groupname' was not closed by \string\endenvironment}\fi \def\@groupname{#1}% \begingroup \let\@groupname = \undefined \ignorespaces}% \def\endenvironment#1{\endgroup \def\@thearg{#1}% \ifx\@groupname\@thearg \else \ifx\@groupname\undefined \errhelp = \@isolatedendenvironmenthelp \errmessage{Isolated \string\endenvironment\space for `#1'}% \else \errhelp = \@mismatchedenvironmenthelp \errmessage{Environment `#1' ended, but `\@groupname' started}% \endgroup % Probably a typo in the names. \fi \fi \let\@groupname = \undefined \ignorespaces}% | %We also define help messages for each of the errors above. %^^|\newhelp| 你也可以给上述这些错误定义它们各自的帮助信息。 ^^|\newhelp| \csdisplay \newhelp\@unnamedendgrouphelp{% Most likely, you just forgot an^^J% \string\endenvironment. Maybe you should try inserting another^^J% \string\endgroup to recover.}% \newhelp\@isolatedendenvironmenthelp{% You ended an environment X, but^^J% no \string\environment\space to start it is anywhere in sight.^^J% You might also be at an \string\endenvironment\space that would match^^J% a \string\begingroup, i.e., you forgot an \string\endgroup.}% \newhelp\@mismatchedenvironmenthelp{% You started an environment X, but^^J% you ended it with Y. Maybe you made a typo in one or the other^^J% of the names.}% | %Some environments should not be allowed to occur within %another environment. Let's call these environments %``outer environments''. |\check!-env| checks that no outer environment %is currently in effect and complains if one is. To use |\check!-env|, you %must issue the command |\environment!-true| at the beginning of every %outer environment. 某些环境应当不允许出现在其他环境内部。我们称这些环境为``外部环境''。 |\check!-env| 检测是否不存在当前有效的外部环境,若存在的话给出警告信息。 要使用 |\checkenv|,你必须在每个外部环境开头处执行 |\environment!-true| 命令。 \csdisplay \newif\ifenvironment \def\checkenv{% \ifenvironment \errhelp = \@interwovenenvhelp \errmessage{Interwoven environments}% \endgroup \fi}% \newhelp\@interwovenenvhelp{% Perhaps you forgot to end the previous^^J% environment? I'm finishing off the current group,^^J% hoping that will fix it.}% | %\section Justification \section 对齐 %\bix^^{flush left} %\bix^^{flush right} %\bix^^{centered text} %The three macros \pix^|\flushleft|, \pix^|\flushright|, and %\xrdef{eplaincenter} %\pix^|\center| justify the text on the following lines in the indicated way. %The command should appear on a line by itself. %Both the command and the text should be enclosed in a group---% %the end of the group indicates the end of the text. %The entire group is set as a single paragraph, with lines filled out %on one side or another as appropriate. Blank lines are reproduced. \bix^^{居左对齐} \bix^^{居右对齐} \bix^^{居中文本} 这三个宏 \pix^|\flushleft|、\pix^|\flushright| 和 \xrdef{eplaincenter}\pix^|\center| 将后面各行文本以指定方式对齐。 这种命令必须单独写在一行中。 命令和文本应该包含在一个编组中——编组的结束就表示文本的结束。 整个编组作为一个段落排版,各行视情况在一边或两边用空白填充。 空行依原样显示。 \csdisplay \begingroup \catcode `\^^M = \active \globaldefs = 1 % \def\flushleft{\beforejustify % \aftergroup\@endflushleft % \def^^M{\null\hfil\break}% \def\@eateol^^M{}\@eateol}% \def\flushright{\beforejustify % \aftergroup\@endflushright % \def^^M{\break\null\hfil}% \def\@eateol^^M{\hfil\null}\@eateol}% \def\center {\beforejustify % \aftergroup\@endcenter % \def^^M{\hfil\break\null\hfil}% \def\@eateol^^M{\hfil\null}\@eateol}% \endgroup | %The following commands are called as a result of the |\after!-group| %^^|\aftergroup| %in the definitions of |\flush!-left|, |\flush!-right|, and |\center|. %They perform the necessary cleanup operations. 在 |\flush!-left|、|\flush!-right| 和 |\center| 的定义中, 下述这些命令出现在 ^|\aftergroup| 的后面; 它们在编组结束后被调用,以执行一些必需的清理工作。 \csdisplay \def\@endflushleft{\unpenalty {\parfillskip = 0pt plus 1 fil\par}% \ignorespaces}% \def\@endflushright{% % Remove the \hfil\null\break we just put on. \unskip \setbox0=\lastbox \unpenalty % We have fil glue at the left of the line; % \parfillskip shouldn't affect that. {\parfillskip = 0pt \par}\ignorespaces}% \def\@endcenter{% % Remove the \hfil\null\break we just put on. \unskip \setbox0=\lastbox \unpenalty % We have fil glue at the left of the line; % \parfillskip must balance it. {\parfillskip = 0pt plus 1fil \par}\ignorespaces}% \def\beforejustify{% \par\noindent \catcode`\^^M = \active \checkenv \environmenttrue}% | %\eix^^{flush left} %\eix^^{flush right} %\eix^^{centered text} \eix^^{居左对齐} \eix^^{居右对齐} \eix^^{居中文本} %\section Tables \section 表格 %The \pix^|\makecolumns| macro enables you to give all the entries in a %table without having to worry about where the columns break. For %example, if you're typing a long alphabetical list that will be %formatted in several columns, you usually won't know in advance where %one column ends and the next begins. Moreover, if another item gets %added, the column breaks will change. \pix^|\makecolumns| 宏允许你给出表格的所有元素,而无需担心如何分栏。 例如,在输入一个冗长的按字母排序的名单,并分多栏显示时, 你通常不知道在何处结束一栏开始下一栏。 另外,如果添加了另一项,各栏的划分将会改变。 %|\makecolumns| takes two (delimited) arguments: the total number of %entries in the table and the number of columns in the table. For %example, `|\makecolumns 37/3:|' specifies a three-column table whose %entries are the next $37$ lines. You can adjust the positioning of the %table on the page by changing |\parindent|, which determines the space %to the left, and |\hsize|, which determines the space from the left %margin of the page to the right of the block. You can allow a page %break above the |\valign| %^^|\valign//used in {\tt\\makecolumns}| %by changing %\pix^|\abovecolumnspenalty|. |\makecolumns| 有两个(定界)参量:表格元素的总数以及表格的栏数。 例如,`|\makecolumns 37/3:|' 指定一个三栏表格,其元素为接下来的 $37$ 行。 要调整表格在页面上的位置,你可以修改 |\parindent| 以确定表格左侧的空白, 以及 |\hsize| 以确定从页面左边缘到表格右侧的距离。 要允许在 |\valign| ^^|\valign//用于 {\tt\\makecolumns}| 之前分页, 你可以修改 \pix^|\abovecolumnspenalty|。 \csdisplay \newcount\abovecolumnspenalty \abovecolumnspenalty = 10000 \newcount\@linestogo % Lines remaining to process. \newcount\@linestogoincolumn % Lines remaining in column. \newcount\@columndepth % Number of lines in a column. \newdimen\@columnwidth % Width of each column. \newtoks\crtok \crtok = {\cr}% \def\makecolumns#1/#2: {\par \begingroup \@columndepth = #1 \advance\@columndepth by #2 \advance\@columndepth by -1 \divide \@columndepth by #2 \@linestogoincolumn = \@columndepth \@linestogo = #1 \def\@endcolumnactions{% \ifnum \@linestogo<2 \the\crtok \egroup \endgroup \par % End \valign and \makecolumns. \else \global\advance\@linestogo by -1 \ifnum\@linestogoincolumn<2 \global\@linestogoincolumn = \@columndepth \the\crtok \else &\global\advance\@linestogoincolumn by -1 \fi \fi}% \makeactive\^^M\letreturn\@endcolumnactions \@columnwidth = \hsize \advance\@columnwidth by -\parindent \divide\@columnwidth by #2 \penalty\abovecolumnspenalty \noindent % It's not a paragraph (usually). \valign\bgroup &\hbox to \@columnwidth{\strut ##\hfil}\cr }% The next end-of-line starts everything going. | %\section Footnotes \section 脚注 %\null ^^{footnotes} %Footnotes are most commonly typeset by using a raised number as the %reference mark. We define the \pix^|\numberedfootnote| macro to do %this. It also redefines ^|\vfootnote| to allow slightly more general %formatting of footnotes than \plainTeX\ does. The dimension register %\pix^^|\footnotemarkseparation| |\foot!-note!-mark!-sepa!-ra!-tion| %controls the space between the footnote mark (e.g., the number) and the %beginning of the text. The \pix^|\everyfootnote| tokens are inserted %before producing the footnote. \null ^^{脚注} 脚注通常用升高的数字作为参考符号。 我们定义的 \pix^|\numberedfootnote| 宏就是这样的。 它还重新定义了 ^|\vfootnote|,以支持比 \plainTeX\ 更一般的脚注格式。 尺寸寄存器 \pix^^|\footnotemarkseparation| |\foot!-note!-mark!-sepa!-ra!-tion| 控制脚注符号(比如数字)与脚注文本之间的间隔。 \pix^|\everyfootnote| 记号在每个脚注之前插入。 %The \plainTeX\ definitions of |\footnote| and |\vfootnote| are %preserved in |\@plain!-footnote| and |\@plain!-vfootnote| in case you should %need them. \PlainTeX\ 中的 |\footnote| 和 |\vfootnote| 定义保留在 |\@plain!-footnote| 和 |\@plain!-vfootnote| 中,以备不时之需。 \csdisplay \newcount\footnotenumber \newtoks\everyfootnote \newdimen\footnotemarkseparation \footnotemarkseparation = .5em \let\@plainfootnote = \footnote \let\@plainvfootnote = \vfootnote \def\vfootnote#1{\insert\footins\bgroup \interlinepenalty\interfootnotelinepenalty \splittopskip\ht\strutbox \splitmaxdepth\dp\strutbox \floatingpenalty\@MM \leftskip\z@skip \rightskip\z@skip \spaceskip\z@skip \xspaceskip\z@skip \everypar = {}% \the\everyfootnote \indent\llap{#1\kern\footnotemarkseparation}\footstrut \futurelet\next\fo@t}% \def\numberedfootnote{\global\advance\footnotenumber by 1 \@plainfootnote{$^{\number\footnotenumber}$}}% | %\section Double columns \section 双栏 %\null ^^{double columns} The |\doublecolumns| command begins %double-column output, while the |\single!-column| %\pix^^|\singlecolumn| %command restores %single-column output. You can switch back and forth between them %on a single page. %The glue specified by |\above!-double!-column!-skip| and %|\below!-double!-column!-skip| is inserted before and after the %double-column material. \null ^^{双栏} |\doublecolumns| 命令开始双栏输出, 而 |\single!-column| \pix^^|\singlecolumn| 命令恢复单栏输出。 两者可以在同个页面上相互切换。 用 |\above!-double!-column!-skip| 和 |\below!-double!-column!-skip| 指定的粘连插入到双栏素材的前面和后面。 %The approach is derived from \knuth{page~417}. 它们的实现方式来源于\knuth{第~417~页}。 \csdisplay \newskip\abovedoublecolumnskip \newskip\belowdoublecolumnskip \abovedoublecolumnskip = \bigskipamount \belowdoublecolumnskip = \bigskipamount \newdimen\gutter \gutter = 2pc \newdimen\doublecolumnhsize \doublecolumnhsize = \hsize \newbox\@partialpage \newdimen\singlecolumnhsize \newdimen\singlecolumnvsize \newtoks\previousoutput \def\doublecolumns{\par % Don't start in horizontal mode. \previousoutput = \expandafter{\the\output} \advance\doublecolumnhsize by -\gutter \divide\doublecolumnhsize by 2 \output = {\global\setbox\@partialpage = \vbox{\unvbox255\vskip\abovedoublecolumnskip}}% \pagegoal = \pagetotal \break % Expands \output above. \output = {\doublecolumnoutput}% \singlecolumnhsize = \hsize \singlecolumnvsize = \vsize \hsize = \doublecolumnhsize \vsize = 2\vsize}% | %The |\@double!-column!-split| macro does the actual splitting. %Insertions are assumed to be single-column material. If you don't want %this to be the case, you'll have to modify the output routine. After %|\@double!-column!-split| has done its work, |\box255| will have the %double-column material. The double-column material will be preceded by %any single-column material that was typeset before |\doublecolumns| was %invoked. |\box4| will have the material that didn't fit on the page. |\@double!-column!-split| 宏执行实际的分栏。插入项被当作单栏素材; 如果这不是你想要的,你需要修改输出例行程序。 在 |\@double!-column!-split| 完成之后,|\box255| 将包含双栏素材。 双栏素材前面是调用 |\doublecolumns| 前的单栏素材。 |\box4| 将包含无法放入该页面的素材。 \csdisplay \def\@doublecolumnsplit{% \splittopskip = \topskip \splitmaxdepth = \maxdepth \dimen0 = \singlecolumnvsize \advance\dimen0 by -\ht\@partialpage \advance\dimen0 by -\ht\footins \advance\dimen0 by -\skip\footins \advance\dimen0 by -\ht\topins \begingroup \vbadness = 10000 \global\setbox1=\vsplit255 to \dimen0 \wd1=\hsize \global\setbox3=\vsplit255 to \dimen0 \wd3=\hsize \endgroup \global\setbox4=\vbox{\unvbox255 \penalty\outputpenalty}% \global\setbox255=\vbox{\unvbox\@partialpage \hbox to \singlecolumnhsize{\box1\hfil\box3}% \vfill}}% | %\needspace{.5in} %|\double!-column!-out!-put| is the real output routine. We call the old %|\output| to do the work of actually shipping out the box. \needspace{.5in} |\double!-column!-out!-put| 是真正的输出例行程序。 我们调用 |\output| 执行实际的盒子送出工作。 \csdisplay \def\doublecolumnoutput{\@doublecolumnsplit \hsize = \singlecolumnhsize \vsize = \singlecolumnvsize \previousoutput \unvbox4}% | %|\singlecolumn| resumes typesetting in one column. It assumes that %|\doublecolumns| has been called. |\singlecolumn| 恢复单栏排版。它假定已调用了 |\doublecolumns|。 \csdisplay \def\singlecolumn{\par % Don't start in horizontal mode. \output = {\global\setbox1 = \vbox{\unvbox255\vskip\abovedoublecolumnskip}}% \pagegoal = \pagetotal \break \setbox255 = \box1 {\singlecolumnvsize = \ht255 \divide\singlecolumnvsize by 2 \advance\singlecolumnvsize by +\ht\@partialpage \advance\singlecolumnvsize by +\ht\footins \advance\singlecolumnvsize by +\skip\footins \advance\singlecolumnvsize by +\ht\topins \@doublecolumnsplit}% \hsize = \singlecolumnhsize \vsize = \singlecolumnvsize \output = \expandafter{\the\previousoutput}% \unvbox255}% | %\margin{`Sensible paragraph skips' section deleted} \margin{`Sensible paragraph skips' section deleted} %\section Finishing up \section 收尾 %We now must undo the changes that we made when we started (see %\xref{eplainconv}). We also give a version identification, which is %subsequently available in |\fmt!-name| and |\fmt!-version|. 现在我们必须撤销开始的修改(见\xref{eplainconv})。 我们还给出版本标识,它可以在 |\fmt!-name| 和 |\fmt!-version| 中得到。 \csdisplay \let\wlog = \@plainwlog \catcode`@ = \other \def\fmtname{eplain}% {\edef\plainversion{\fmtversion}% \xdef\fmtversion{1.0: 15 May 1990 (and plain \plainversion)}% }% | %\eix^^|eplain.tex| \eix^^|eplain.tex| \ifoldeplain\else\ifcompletebook\else \vskip4em{\sectionfonts\leftline{本章索引}} \readindexfile{i} \fi\fi \endchapter \byebye