在 TeX 中,“\romannumeral1986” 不等于“mcmlxxxvi”吗?

在 TeX 中,“\romannumeral1986” 不等于“mcmlxxxvi”吗?

在 TeX 中,

\uppercase{\romannumeral1986}\par
%\catcode`\m=12
\uppercase{mcmlxxxvi}
\bye

生产

在此处输入图片描述

那么,不mcmlxxxvi等于\romannumeral1986? 它们之间有什么区别?\expandafter可以绕过这个问题。\uppercase\expandafter{\romannumeral1986}会产生MCMLXXXVI。原因是什么?

答案1

原语\uppercase不会扩展其参数。它所做的是获取范围内的每个字符标记a-z并将其替换为大写字母。因此,当您发出

\uppercase{\romannumeral1986}

你正在\uppercase对一组标记执行操作,其中第一个是原始的\romannumeral,后跟1,和98和和6。这些都不是在乎的 26 个字符之一\uppercase,所以它什么也不做。所以输出正是\romannumeral1986

正如你所看到的,如果你\expandafter首先使用它来扩展论点,你就会得到预期的效果。


另外,使用解开包裹,也许看到它的实际作用会让我们更容易理解 TeX 的设计。

答案2

当读者第一次阅读第 7 章时,他们不应该知道接下来的所有内容,事实上,练习 7.9 被标记为双重危险。

危险弯角标记非常重要:第一次阅读时,应跳过危险弯角;第二次阅读时,可以查看危险弯角并跳过危险倍增的弯角。第三次(或第四次)阅读时,应能够面对危险倍增的部分。

如果您不理解开头的危险段落,请不要感到难过:您不应该理解。


现在来谈谈一些完全不同的事情:让我们根据 TeXbook 从前言到附录 J 的知识来解释一下发生了什么。

原语\uppercase\lowercase的行为非常独特。首先,让我们看看它们的语法:

\uppercase⟨一般文本⟩
\lowercase⟨一般文本⟩

⟨一般文本⟩ → ⟨填充符⟩ {⟨平衡文本⟩⟨右括号⟩
⟨填充符⟩ → ⟨可选空格⟩ | ⟨填充符⟩ ⟨\relax可选空格⟩

最后,⟨平衡文本⟩是任何关于以下方面平衡的标记序列:明确的 {}

与其他需要⟨一般文本⟩的原语相比,这两个原语的区别在于,⟨平衡文本⟩被直接发送到“胃”(因此不发生宏扩展),其中标记以特殊方式处理:字符标记根据和转换为大写或小写\uccode\lccode而任何其他标记保持不变。请注意,类别代码是不是已改变。

然后将这样获得的标记列表发送回“口”,在那里可以照常进行宏扩展。

由于\uccode数字的为 0,因此它们不会发生变化\uppercase。因此代码

\uppercase{\romannumeral1986}

只是一种低效的方式来获得相同的结果

\romannumeral1986

那么,如何从 1986 年获得“MCMLXXXVI”?⟨filler⟩ 就是答案,一旦你知道,当 TeX 寻找{包含 ⟨balanced text⟩ 的 时,它会执行扩展,直到找到一些不符合空格标记的标记,如\relax{。但是,空格标记和\relax标记会被忽略,然后重新开始查找{。如果 TeX 最终找不到{,则会引发错误。

这就解释了为什么

\uppercase\expandafter{\romannumeral1986}

实际上产生了 token MCMLXXXVI(类别代码均为 12)。事实上,在找到 a并丢弃 ⟨filler⟩\expandafter的过程中, 被扩展,并且根据规则,在触发下一个 token 之后,其扩展为空,在本例中为 。因此,当该过程实际找到 时,⟨balanced text⟩ 已经变成{\romannumeral{

mcmlxxxvi

因此胃中的处理将发回令牌列表

MCMLXXXVI

将会被正常处理。

实际上练习 7.9 的答案应该是

\uppercase\expandafter{\romannumeral\year}

但这只是一个细节。

锻炼。解释一下

\uppercase\ifodd\day\expandafter\expandafter\fi{\romannumeral\year}

产生的原因以及为什么\expandafter使用两个标记。

相关内容