我正在读马尔科姆·克拉克的简单的 TeX 入门。第 111 页有一个练习,定义一个打印当前时间的宏。我在下面重现了 Clark 的回答:
\newcount\minleft
\newcount\timehour
\def\thetime{\timehour=\time
\divide\timehour by60 %gives 24 hour part of clock
\minleft=\timehour
\multiply\minleft by-60 %
\advance\minleft by\time %minutes after hour
\ifnum\time>720\advance\timehour by-12\fi\relax
\number\timehour:\ifnum\minleft<10 %
0\fi\relax\number\minleft
\ifnum\time>720~p.m.\else~a.m.\fi}
我的问题是我不知道\relax
第 8 行和第 10 行(每个之后)在做什么\fi
。当我使用这个宏时,它会按预期打印时间:
晚上 8:51
如果我删除\relax
第 8 行,我会得到时间,但缺少小时:
下午 51 点
如果我删除\relax
第 10 行(并恢复第\relax
8 行),则宏将再次像第一次一样工作:
晚上 8:51
我天真地以为\fi
结束该\ifnum
块的 会结束 中正在发生的一切的处理\ifnum ... \fi
,并且 是否存在并不重要\relax
,在任何一个地方。显然事实并非如此,但我不知道第 8 行发生了什么,也不知道为什么\relax
没有在第 10 行似乎是必要的。
在不同的上下文中,作者确实解释说,这\relax
是一个“不执行任何操作”的命令(在另一种情况下),它阻止 TeX 处理同一表达式的\relax
前后部分。我明白了。但关于之后,他说:\relax
\relax
\fi
“这\relax
可能不是必需的,但有一个总是好的:也许是‘安全 TeX’的一个例子。”
这也许是个好建议,但没有什么启发意义。
我在 tex.se 上找到了以下问题\relax
,但我认为它们没有回答我的问题(或者至少我还不够聪明无法弄清楚):
\relax
那么,这个宏实际上在做什么?
答案1
我不了解这本入门书,但我对任何声称具有指导意义但又乐于建议您“以防万一”做某事的书都有点怀疑。总是有原因的;在这种情况下,原因是 TeX 的典型怪癖之一。
问题不在于 ,\ifnum
而在于\advance
。TeX 以相反贪婪的方式扫描您要前进的量:它会尽可能深入输入流,一路扩展,直到遇到不能成为数字一部分的原始量。然后它将找到的内容作为操作数。以下是表达式
\ifnum\time>720\advance\timehour by-12\fi%\relax
\number\timehour:
被解析。首先,它检查\time
,720
如果是,则开始执行\advance
。它读取-12
然后继续阅读。现在,你错过的\fi
是不是句法:也就是说,它是仍然此时在输入流中,在 TeX 处理 时未被删除\ifnum
。并且它是可扩展的。扩展它确实会终止条件,但分支没有任何词汇范围,因此扩展会继续,\fi
直到\number\timehour
,这是一个可扩展的数量,会变成当前的数字\timehour
。然后是:
,这是不可扩展的和非数字的,因此扫描停止。
结果是,如果\time > 720
,我们前进\timehour
的值-12<value of \timehour before advancing>
。这个整数被吸收,尽管前进后它基本上被丢弃,因为实际使用的唯一代码片段\timehour
已被删除。相反,如果\relax
没有注释,因为它是不是可扩展,它会立即终止扩展\fi
,而\number\timehour
不会被丢弃;由于它是无操作,因此没有其他效果。这解释了为什么在没有它的情况下,小时会从输出中消失,只剩下冒号。
我不认为这是很好的风格。它脱离了\relax
它所保护的东西。也许更好的做法是写
\ifnum\time>720\advance\timehour by-12\relax\fi
\number\timehour:
所以整个短语\advance \timehour by -12\relax
是一个单一的操作。您也可以在 后面放一个空格-12
,但要小心,不要随意丢弃;TeX 对空格的冷漠态度导致了许多编程故障(就像任何语言特性一样,它使得以口语化而不是严格形式化风格编写代码变得容易)。