在 中plain.tex
,DEKnuth 决定将\outer
所有分配寄存器的宏定义为:\newcount
,\newdimen
,\newskip
,\newread
,\newwrite
。这禁止它们进入宏的参数或定义:它们只能出现在最外层。
另一方面,Leslie Lamport\outer
在编写 LaTeX 格式时基本放弃了原语。特别是,\outer
在他的方法中,分配宏已不复存在。
这两种选择各有什么优缺点?其他格式(ConTeXt、LaTeX3)怎么样?
附带问一下:是否有一些与\outer
原始相关的历史民间传说?
答案1
的主要目的\outer
是捕获错误,例如在宏定义中粗心地分配寄存器或流,或者忘记\long
宏参数的右括号。我们经常看到
\newcommand{\xyz}[1]{...\newlength{\xyzlen}\setlength{\xyzlen}{#1}...}
即坏的编程;这在 Plain TeX 中不会发生(当然,使用\newskip
或),因为这些宏是。为什么这是糟糕的编程?因为每次调用 时,都会分配一个新寄存器,而旧的寄存器将被遗忘和无法使用。我们可以将 视为允许接收值的变量:因此\newdimen
\outer
\xyz
\xyzlen
\xyzlen
\newlength{\xyzlen}
\newcommand{\xyz}[1]{...\setlength{\xyzlen}{#1}...}
可以完成同样的工作,而且效率更高。
还必须记住,原始 TeX 每种类型都有 256 个寄存器,但只有 16 个输入和输出流。因此,保留这些寄存器至关重要,而将\new...
宏声明为\outer
有助于避免寄存器滥用。无论如何,这都是糟糕的编程,即使现在 e-TeX 每种类型都提供了 32768 个寄存器。
然而,\outer
使用起来有一些缺点:\outer
宏不能出现在跳过的条件文本的中间,所以
\ifdefined\xyzlen\else\newdimen\xyzlen\fi
\xyzlen
一旦定义,在 Plain (pdf)TeX 中无效。
另一个例子:很难在\beginsection
或之上定义 Plain TeX 宏\proclaim
,比如添加自动编号。
我认为,分配宏应该是外部的。在某些情况下,这可能会造成麻烦。例如,定义新类型的浮点数的包将\newcounter
在定义中使用。提供一个“内部”版本\@newcounter
供包使用会很容易
\outer\def\newcounter{\@newcounter}
\def\@newcounter#1{<what LaTeX does now>}
例如,Plain TeX 表示\outer\def\+{\tabalign}
我们可以\tabalign
在涉及简单对齐的定义中使用。这至少可以捕获缺乏经验的用户在定义命令时犯的错误。
然而,总有一句老话(就像我以前学会的那样)
\newcount\thmcnt
\def\theorem{\advance\thmcnt 1
\csname proclaim\endcsname \number\thmcnt\ }}
这使得 TeX 无法看到禁止的控制序列,\theorem
其行为方式与 类似\proclaim
。但任何了解 的人都\csname
可以称为“经验丰富的用户”,即使不是 TeX 专家。
Bruno 在他的评论中提出了另一种方法:
\edef\innerproclaim{\noexpand\proclaim}
\newcount\thmcnt
\def\theorem{\advance\thmcnt 1 \innerproclaim\number\thmcnt\ }}
\noexpand\proclaim
之所以有效,是因为在将结果赋值为 的含义之前,的扩展已经执行\innerproclaim
,而\noexpand
前面的 使得 TeX 在赋值时看到一个等同于 的控制序列\relax
,而 允许出现在替换文本中。同样,\edef
和\noexpand
行家有资格成为经验丰富的用户。