我记得我曾经写过一些 TeX 代码,遇到过这样的事情
\count\foo
\foo=2\otherMacro
[\the\foo]
如果\otherMacro
被定义为类似于上面那行\def\otherMacro{9 cats is too many}
的东西,那么\foo
它实际上会被设置为 29 而不是 2,因为来自猫宏的 9 会被吸收。
更阴险的情况是这样的:
\newcount\foo
\def\setfoo{\foo=2}
\def\otherMacro{9 cats is too many}
\setfoo\otherMacro
[\the\foo]
此代码也出错了,吸收了 9,将 \foo 设置为 29。在这种情况下,很难知道修复的适当位置在哪里。如果\setfoo\otherMacro
不返回宏定义,很难在查看时发现错误,因为在实际情况下,宏定义可能离调用很远。
我记得在遇到这个问题时,我坐下来全面了解了所有可能发生这种情况的情况,并采用了一套防御性编码标准来防止这种情况发生,其中包括自由使用 % 和空格。但是,我没有做任何笔记,而且我不确定我能记住所有的细节和细微差别。例如,我不记得在处理 \dimen 或其他类型的寄存器时是否发生过这种情况,我也记不清我防御性使用 % 和空格的规则到底是什么。
我正要坐下来再次试验这个 TeX-gotcha,但我认为在 SO 上发帖寻求社区建议也无妨。我知道 SO 通常不喜欢讨论帖子,所以我会这样说:如果有人对这些问题有全面的了解,那么关于这个问题的单篇脑力倾泻将是一个适合 SO 的坚实回应。
还请注意,我使用的是 Plain TeX,而不是 LaTeX。
[更新]:
在考虑了 David Carlisle 建议的解决方案并考虑了他所描述的注意事项之后,我将这个好奇心修改为这篇文章:
\def\otherMacro{9 cats is too many}
\newcount\foo
\def\bar#1{[\the\foo][#1]}
\def\setfooSpace{\foo=2 }
\def\setfooRelax{\foo=2\relax}
\afterassignment\bar\setfooSpace\otherMacro
\afterassignment\bar\setfooRelax\otherMacro
倒数第二行结果为文本
[2][9 cats is too many]
而最后一行本身的结果是
[2][]9 cats is too many
\relax
和解决方案都2<space>
可以防止 9 被 2 吸收,但是带有空格的解决方案似乎更胜一筹,因为它使像 \setfoo 这样的宏感觉更具原子性。
答案1
数字后面的空格总是终止<number>
并被吸收,因此
\foo=2 \otherMacro
将分配 2 给\foo
后面的(没有空格)扩展\othermacro
任何其他不可扩展的非数字标记都会终止<number>
但被替换
\foo=2\relax\otherMacro
分配 2 到\foo
然后标记流具有\relax
和扩展\otherMacro
在任何其他情况下,数字后面的标记都会被扩展,以决定是否有更多的标记需要吸收到数字中。
所以你的例子
\def\setfoo{\foo=2}
是一个即将发生的错误,应该
\def\setfoo{\foo=2 }
或者当然
\def\setfoo{\foo=\tw@}
使用此类标记的主要原因是\tw@
为了避免这些问题,因为它们是完整的<number>
(\chardef
在这种情况下定义为),因此 TeX 不会提前扫描寻找更多数字。