据我了解,的行为\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
。