\expandafter 与 \uppercase 排序

\expandafter 与 \uppercase 排序

据我了解,的行为\expandafter是这样的,如果\a\b是宏,那么将\expandafter\a\b首先扩展。\b\a

但我的理解一定是错的,因为我不明白为什么以下命令

\def\xx{xx}
\expandafter\uppercase{\xx}
\bye

生成“xx”而不是“XX”。

经过谷歌搜索后,似乎正确的方法是将第二行替换为

\uppercase\expandafter{\xx}

确实有效。但是它\expandafter接受两个参数;如果第一个参数是\xx第一个参数,那么第二个参数是什么?

无论如何,既然在这种情况下\xx需要在之后进行扩展\uppercase,为什么第一个版本不起作用——如何解释命令的顺序?

答案1

\expandafter语在一个标记之后展开:它确实不是接受两个参数。因此

\expandafter\uppercase{\xx}

跳过\uppercase并尝试扩展{,这是不可扩展的,所以什么也不会发生。因此,这与简单地编写完全相同

\uppercase{\xx}

因此,一般来说,要在控制序列后扩展参数,你需要使用\expandafter两个

\expandafter\uppercase\expandafter{\xx}

\uppercase如果是宏,则需要这样做,但这里我们讨论的是原语。与其他几个 TeX 原语一样,\uppercase它有一个正式的语法,允许在左括号前进行扩展。可能更常见的情况是赋值toks,其中

\def\xx{xx}
\toks0=\expandafter{\xx}
\showthe\toks0

给出

> xx.

这些原语接受<general text>TeXbook和其他参考文献(我喜欢TeX 按主题分类)。

答案2

全面讨论这个话题确实太长了,所以我将描述一些一般的事实来概括。

一些 TeX 基元需要非常特定的标记来跟在它们后面。为了举一些例子,我会提到\over后面可以跟任何在数学模式下合法的符号,而\uppercase后面则希望看到<general text>。因此,在 的情况下\over,不会立即进行扩展,而只有在 TeX 为分母启动新的数学列表后才会进行扩展。另一方面, a<general text>定义为

<filler>{<balanced text><right brace>

[276](括号中的粗体数字将引用 TeXbook 的页面),这隐含地表示\uppercase触发扩展以查找{(类别代码为 1 的显式或隐式字符)。是将被删除的<filler>任意空格和标记序列\relax。例如[374]

\uppercase\expandafter{\romannumeral\n}

生成存储在大写罗马数字中的值的表示形式\n。另一个示例是让狮子跑来跑去。简洁地

另一个例子是\left语法规则要求遵循<delim> [292],这又[289]

<delim> → <filler>\delimiter<27-bit number> | <filler><letter> | <filler><otherchar>

(在这种情况下,<letter>或当然<otherchar>应该有一个非负的)。因此,类似的东西会起作用,因为正在寻找一个特定的标记(或具有类别代码 11 或 12 的字符标记),并且会扩展,然后忽略适合的结果。和也发生了类似的情况(参见\delcode\left\space[\left\delimiter\space<filler>_^为什么 `x_\text y` 有效?)。

你应该时刻记住 TeX 不扩展标记的地方列表,它位于[215]。例如,TeX 将不是\def执行或时会扩展标记\let。 的语法\let允许<one optional space>在 (可选) 之后=(更准确地说<equals>),但它不会进行扩展:因此

\let\foo=\space\relax

\foo相当于\space,而不是\relax。这是因为\let可以有任何之后的标记<equals>,而不是特定的标记。

原语所需的“特定标记”可以相当通用;例如,\the允许在其后有一组非常大的标记,并且它会扩展标记来寻找它们。构造

\toks@=\expandafter{\the\toks@ x}

将附加x到 的先前内容\toks@。如果我们想附加另一个标记寄存器的内容,比如 ,该怎么办\mytoks?很简单:

\toks@=\expandafter{\the\expandafter\toks@\the\mytoks}

解释:外部\expandafter将触发第一个\the;执行此操作是因为 TeX 正在寻找一个标记寄存器分配{,因此它会在操作过程中扩展标记;现在\the想要查看一个特定的标记,因此它会进行扩展以找到它;因为\expandafter 可展开(具有空展开),它会触发它,从而展开第二个\the。更复杂的版本是

\edef\x{\the\toks@\the\mytoks}\toks@=\expandafter{\x}

或者

\begingroup\edef\x{\endgroup\toks@={\the\toks@\the\mytoks}}\x

(后者不会留下定义\x)。

该构造\expandafter\uppercase{\xx}与 没有什么不同,\uppercase{\xx}因为\expandafter会尝试扩展{,而 是不可扩展的。没有通用的方法可以强制将多个标记扩展一级,这无论如何都没有多大意义。如果您必须将可能隐藏在宏中的字符列表大写,那么只有完全扩展才有意义:

\begingroup\edef\x{\endgroup\uppercase{<tokens>}}\x

(再次没有留下 的定义\x)或

\begingroup\edef\x{<tokens>}\uppercase\expandafter{\expandafter\endgroup\x}

使用临时宏的更简单的命令是

\edef\next{<tokens>}\uppercase\expandafter{\next}

\next但我不推荐这样做(如果之前碰巧是\let不可扩展的标记,麻烦就在后面)。在 LaTeX 中,应该使用\protected@edef, 而不是\edef

相关内容