TeX 定义了一些原语,其中一些原语在执行自身之前会扩展(或尝试)以下标记;其他原语甚至要求在括号之间添加参数。但在这种情况下我无法理解这一点。
\uppercase\expandafter{\romannumeral\year} % a cut off from https://www.tug.org/utilities/plain/cseq.html#uppercase-rp
\大写要求在括号中输入参数(运行\uppercase abcdef
一下你就会明白我的意思)。但在上面的突出显示的示例中,\大写正在获得可扩展(?)原语作为参数并且正确执行。
仔细观察后我发现
\def\seq{abcdef}%
\uppercase\seq %
会产生错误,但是
\def\seq{{abcdef}}%
\uppercase\seq %
没有。
我遗漏了一点。哪一点?有人能解释一下这种行为吗?大括号被视为特殊情况还是可扩展性受限的情况?
答案1
这是 TeX 的一个有趣部分。一些原语需要括号中的参数。例如\uppercase
,,\vbox
。\halign
但是,它们的工作方式与宏不同。例如,左括号也可以隐式(\bgroup
是隐式左括号)并且它们对以下标记进行完全扩展,直到找到左括号。在此过程中,\relax
空格标记将被丢弃。
更准确地说\vbox
,,\hbox
并且\halign
接受关键词和括号前的值。因此,如果您这样做\def\test#1{to #1pt}
,您可以这样做
\vbox\test{90}{<balanced text>}
这将导致\vbox to 90pt{<balanced text>}
。请注意,其中一些原语接受隐式闭括号,但有些则不接受。在第一类中,有\vbox
、\hbox
和\halign
,因为<material>
会被处理直到找到匹配的闭括号;在第二类中,你会发现\uppercase
、\lowercase
和\toks
,因为它们根本不处理<balanced text>
。此外,\toks
你还会发现所有原语标记寄存器,例如\everypar
和 所有\toksdef
标记。
这是允许
\uppercase\expandafter{\romannumeral 42}
结果是XXXXII
。实际上,\uppercase
触发了以下标记的扩展,并且\expandafter
在触发了其后的第二个标记的一步扩展之后 的扩展是空洞的(在查找两个标记时没有发生宏扩展)。
因此
\def\testA{abc}
\def\testB{{abc}}
\uppercase\testA
\uppercase\testB
第一个调用会失败,因为在宏扩展后它的结果为\uppercase abc
,但第二个调用会成功,因为它的结果为 ,\uppercase{abc}
这是合法的。请注意,结束括号是不是隐式的,因为在扩展的时候明确地把它放到了主流中\testB
。
为什么\relax
和空格标记会被忽略?假设你这样做
\def\myspace{50pt}
如果你想在上下文中使用\hskip\myspace
,你可能会遇到类似这样的问题
We have \hskip\myspace\space minuscule chances
(尝试一下)。因此更好的定义是
\def\mysspace{50pt\relax}
上述示例将顺利完成。但你可能还想这样做
\hbox to \myspace{<text>}
如果不是因为在寻找此类标记时忽略该标记\relax
的规则,那么 就不是很好。它在 TeXbook 中被称为 。{
<filler>
所以,牙套不是“可扩展的”。
答案2
您的实验\uppercase\seq
是这里的重要线索。\uppercase
希望其参数括在括号中,因此如果它在括号后找到控制序列,它将在寻找其左括号时将其展开。因此,使用\uppercase\seq
,它会展开\seq
,因此
\def\seq{abcdef}%
\uppercase\seq %
是相同的
\uppercase abcdef
尽管
\def\seq{{abcdef}}%
\uppercase\seq %
相当于
\uppercase{abcdef}
另一方面,当你有
\uppercase\expandafter{\romannumeral\year} % a cut off from https://www.tug.org/utilities/plain/cseq.html#uppercase-rp
被\expandafter
扩展了,这意味着它忽略了花括号,扩展了\romannumeral\year
(将给出ccxxi
接下来的 81 天),\uppercase
然后进行操作
\uppercase{ccxxi}
\expandafter
然而,如果没有
\uppercase{\romannumeral\year}
将看不到任何可以大写的内容,并且输出也不会是CCXXI
但是ccxxi
。
答案3
让我们看一下 TeXbook,第 24 章:垂直模式摘要——您可以在其中找到以下信息片段:
\uppercase⟨general text⟩
, 。 这\lowercase⟨general text⟩
⟨平衡文本⟩在里面 ⟨一般文本⟩\uccode
按照第 7 章的说明,使用或 表将其转换为大写形式或小写形式\lccode
;不进行扩展。然后 TeX 将读取 ⟨平衡文本⟩再次。
(为了精确起见,不应该只是“;没有进行扩展”,而应该是“;⟨平衡文本⟩没有进行任何扩展”。)
⟨一般文本⟩→⟨填料⟩{⟨平衡文本⟩⟨右括号⟩;左括号标记可以是隐式的。
⟨填料⟩→⟨可选空格⟩ | ⟨填料⟩\relax
⟨可选空格⟩
⟨可选空格⟩→⟨空的⟩ | ⟨太空代币⟩⟨可选空格⟩
⟨空的⟩→无
⟨太空代币⟩→“要么是类别 10 的字符标记,要么是控制序列或活动字符,其当前含义已通过\let
或等于此类标记\futurelet
。”
⟨平衡文本⟩是一个标记列表“其中⟨左括号⟩和⟨右括号⟩如果存在的话,标记会像括号一样正确嵌套。
“⟨左括号⟩和⟨右括号⟩是显式字符标记,其类别代码分别为类型 1 和 2”。(1=组开始。2=组结束。)
让我们看一下 TeXbook,第 20 章:定义(也称为宏)——您可以在其中找到以下信息片段:
这里现在是可扩展令牌未扩展的所有情况的承诺列表。
[...]
- 当 TeX 吸收
\def
或\gdef
或\read
; 的替换文本或标记变量的文本(如\everypar
或\toks0
; 或 代币列表或\uppercase
或\lowercase
。\write
( 的标记列表\write
将在稍后实际输出到文件时进行扩展。)[...]
(请注意,在可扩展标记不扩展/扩展被抑制的情况列表中,TeX 扫描左括号标记的情况⟨一般文本⟩本身不是一个物品。
让我们非常挑剔地看待这些信息片段所形成的“图画”:
在可扩展标记不扩展/扩展被抑制的情况列表中,TeX 扫描左括号标记的情况⟨一般文本⟩本身不是一个物品。
因此,在扫描属于以下内容的左括号标记时,不会抑制\uppercase
/扩展\lowercase
⟨一般文本⟩:TeX 持续扩展可扩展标记,直到出于某种原因需要引发错误消息,或者 TeX 发现⟨一般文本⟩其后的左括号标记⟨平衡文本⟩随后,又跟着一个⟨右括号⟩。 那⟨平衡文本⟩是具有以下属性的标记列表⟨左括号⟩和⟨右括号⟩如果存在,标记会像括号一样正确嵌套。这就是为什么在第 20 章中⟨平衡文本⟩被称为“token list”。
根据第 20 章,构成该列表的 token⟨平衡文本⟩\uppercase
/\lowercase
工作时将不会进一步扩展。
当需要根据可能出现在书中相距甚远的章节中的个别信息来形成整体画面时,需要对 TeXbook 中的每个短语都极其挑剔,这有时让我建议人们像一个优秀的律师审查合同一样阅读 TeXbook,仔细挑剔所呈现的方面和相关性,以发现和避免以可能的解释错误形式出现的潜在陷阱。
Knuth 在 TeXbook 的前言中解释道,属于上下文的所有信息并不总是立即给出,而是一点一点给出的:
本手册的另一个值得注意的特点是它并不总是说实话。当非正式地介绍 TeX 的某些概念时,会陈述一般规则;之后你会发现这些规则并不完全正确。一般来说,后面的章节包含的信息比前面的章节更可靠。作者认为这种故意撒谎的技巧实际上会让你更容易学习这些想法。一旦你理解了一个简单但错误的规则,用它的例外来补充这条规则并不难。
和
\uppercase\expandafter{\romannumeral\year}%
TeX 扫描\uppercase
's⟨一般文本⟩的左括号标记,因此扩展不会被抑制。因此\expandafter
被扩展。扩展的结果\expandafter
是扩展的结果\romannumeral
。\romannumeral
反过来会触发对 TeX- 的扫描⟨数字⟩\year
- 由此发现并扩展的数量。
所以最后⟨一般文本⟩的⟨平衡文本⟩,紧随其后的是⟨一般文本⟩的左括号标记({
)和⟨一般文本⟩的⟨右括号⟩( }
),您将获得类别代码为 12(其他)的显式字符标记,以小写罗马符号表示年份。
根据第 20 章,没有尝试扩展该 ⟨平衡文本⟩继续\uppercase
“工作”。(在这种情况下,这种尝试将是徒劳的,因为在这种情况下⟨平衡文本⟩仅由不可扩展的显式字符标记组成。
和
\def\seq{abcdef}%
\uppercase\seq
TeX 扫描\uppercase
's⟨一般文本⟩的左括号标记。因此扩展不会被抑制。因此\seq
被扩展。现在 TeX 既找不到另一个可扩展标记,也找不到⟨填料⟩也不是属于⟨一般文本⟩但确实找到了类别代码 11(字母)的明确字符标记a
,因此引发了错误。
和
\def\seq{{abcdef}}%
\uppercase\seq
TeX 扫描\uppercase
's⟨一般文本⟩的左括号标记。因此扩展不会被抑制。因此\seq
被扩展。因此 TeX 确实找到了序列的左括号标记{abcdef}
,并将其视为⟨一般文本⟩.abcdef
该序列被视为⟨平衡文本⟩的⟨一般文本⟩.}
该序列被视为⟨右括号⟩的⟨一般文本⟩。因此语法没有问题,不需要引发任何错误。
因为⟨填料⟩-事物⟨一般文本⟩你也可以
\def\seq{ \relax {abcdef}}%
\uppercase\seq %
\uppercase
扫描时⟨一般文本⟩的左括号标记,在 的定义左括号后面出现的显式空格标记\seq
和后续的空格标记都\relax
将被丢弃为 ⟨填料⟩。
请注意,除了\uppercase
和 之外\lowercase
,TeX 中还有更多处理⟨一般文本⟩因此,在尝试做实际工作之前,扩张不会被抑制,直到找到⟨一般文本⟩的左括号标记。