我主要使用 Plain TeX。最近我对\outer
的性质感到恼火\newcount
,尤其是当我编写需要临时内部变量的宏时,这些变量实际上不需要是全局变量。我知道我可以\count0
从技术上\count9
将 用作临时变量,但当我TeXbook提到它们也被用作页面计数器,以及当触发页面发运时使用它们会弄乱页码。
所以,我的问题是:为什么\newcount
声明为\outer
?我已经检查过,使用 创建的名称\countdef
确实可以在组内本地作用域,并且我知道对\count
寄存器的更改同样是本地作用域。那么为什么 Knuth 选择\newcount
使用\outer
限制进行标记?是否有任何东西阻止我定义自己的\countdef
基于 的分配器,特别是允许使用组本地名称的分配器。
答案1
从技术层面上讲,制作宏\outer
并不意味着添加\def
任何东西:它纯粹是一个限制(与 的子集非常相似\long\def
)。因此,这里的推理是为了确保输入遵循特定的模式。
寄存器分配在 plain、LaTeX 和 ConTeXt 中(至少从根本上讲)的实现方式大致相同,但只有 plain 会使用\outer
宏来实现。因此,从技术意义上讲,这里使用“普通”宏并不存在“限制”。但使用宏确实\outer
可以防止类似
\def\foo#1{%
\newcount\mycount
...
}
您偶尔会认为这是对 TeX 任务范围的误解。(我假设这里\foo
在文档中使用多次。)
当然,您仍然可以使用\outer
以下方式在其他宏中使用宏\csname
:
\def\foo#1{%
\csname newcount\endcsname\mycount
...
请注意,LaTeXetex
包和“e-plain”格式都包含允许“本地寄存器分配”的代码:etex
包代码包含非常小 LaTeX,所以也许可以看看\loccount
那个(例如 less `kpsewhich etex.sty`
在 *nix 上)。
答案2
计数器非常宝贵,因为它们只有 256 个,而且其中有几个已经被分配或被 TeX 使用:前十个是为页面计数器保留的,这是 DVI 格式的要求;从 10 到 21 的计数器用于与寄存器分配相关的簿记,并\count255
作为临时寄存器保存。Plain 格式保留了另外六个(两个通过 隐式保留\newinsert
)。
注意:这里我是从使用没有 e-TeX 扩展的原始程序运行的 Plain TeX 的角度出发的。
虽然可用计数器的数量似乎仍然很大,但必须记住,\newcount
指令会执行全局分配。让我们看看:
\outer\def\newcount{\alloc@0\count\countdef\insc@unt}
\def\alloc@#1#2#3#4#5{\global\advance\count1#1by\@ne
\ch@ck#1#4#2% make sure there's still room
\allocationnumber=\count1#1%
\global#3#5=\allocationnumber
\wlog{\string#5=\string#2\the\allocationnumber}}
因此\newcount\foo
\global\advance\count10 by 1
\ch@ck0\ins@count\count
\allocationnumber=\count10
\global\countdef\foo=\allocationnumber
\wlog{\string\foo=\string\count\the\allocationnumber}
我们发现上面的几行
\count10=22 % allocates \count registers 23, 24, ...
所以我们知道\count10
存储了最后分配的计数寄存器的编号。用户空间中第一个可用的是编号 23,但 Plain TeX 有三个\newcount
指令。因此\newcount\foo
将打印
\foo=\count26
在日志文件中。
现在您看到寄存器的分配是连续的。如果不使用池而不是连续策略彻底更改宏,就无法“取消分配”寄存器。
宏定义如下
\def\amacro#1{\newcount\temp \temp=#1\relax ...}
每次调用时都会全局浪费一个寄存器:相信我,我在 LaTeX 宏中见过几次这种错误;正如你所知道的,\newcounter
在LaTeX 中\newcount
并不存在\outer
。有时是\newlength
,但想法是一样的。
\newcount
通过将和其他类似的分配宏声明为\outer
,这种错误(几乎)不可能出现。
请注意,对于 e-TeX 允许的 32768 个寄存器或 LuaTeX 提供的 65536 个寄存器,这也是一个错误。这些寄存器稍后才会耗尽。
在某些情况\newcount
下,宏是合法的:如果我们想定义一个接口,比如引入分层分段命令,我们必须允许它\newcount\subsectioncount
作为(虚构的)命令的一部分
\definelevel{subsection}{...}
但这里不是“用户空间”: 的定义\definelevel
是由程序员做出的,他知道(或至少应该知道)正在做什么。程序员会输入
\csname newcount\expandafter\endcsname\csname#1 count\endcsname
在 的代码中\definelevel
,覆盖 的外部性\newcount
。