为什么 \expandafter 可以与 \uppercase 一起使用,但不能与 \textbf 一起使用?
考虑以下 MWE:
\documentclass{article}
\begin{document}
\def\name{hello world}
\textbf\expandafter{\name}
\uppercase\expandafter{\name}
\end{document}
答案1
任何 TeX原始需要⟨一般文本⟩的函数有同样的特点:为了找到原语应该操作的标记,TeX 会执行宏扩展,忽略空格和\relax
标记,直到找到{
(意味着找到类别代码为 1 的明确字符标记或此类字符的标记\let
)。
此外\uppercase
\lowercase
- ⟨token 变量⟩
\message
和\errmessage
\write
\special
\mark
和\marks
(*)\unexpanded
(*)\detokenize
(*)\scantokens
(*)\showtokens
(*)\expanded
(*)
具有相同的属性。标有 (*) 的图元不在 Knuth TeX 中。请注意,⟨一般文本⟩ 必须以 ⟨右括号⟩ 结尾,这意味着明确的第 2 类字符标记。例如
\def\foo{{foo}}
\uppercase\foo
\uppercase\relax\foo
将导致打印FOOFOO
,但请注意 定义中的内部括号\foo
。使用
\uppercase\expandafter{\name}
类似:由于\expandafter
是可扩展的,TeX 会执行相关操作,即扩展(一级)下一个标记(因此{
跳过并\name
扩展)并消失。因此主标记列表具有
\uppercase{hello world}
并\uppercase
开始工作:TeX 查找匹配的}
,将标记发送到肠胃,肠胃将应用\uccode
等效项并将结果发送回主标记列表;\uppercase
现在括号已经消失,结果是
HELLO WORLD
如果没有\expandafter
,胃就会看到\name
它什么也不做,因为没有字符标记,因此它会被不加改变地送回。
什么是 ⟨token 变量⟩?例如\everymath
,
\everymath=\expandafter{\the\everymath ⟨other tokens⟩}
会将 ⟨其他标记⟩ 附加到 的当前内容中\everymath
(=
是可选的)。任何 ⟨标记参数⟩、\toks⟨number⟩
命令和所有用 定义的标记\toksdef
(通常通过 定义\newtoks
)都是 ⟨标记变量⟩ 的实例;此外,在 TeXbook 第 275 页还列出了 的\everymath
其他八个实例。⟨token parameter⟩
相反,\textbf
从实际角度来看,这是一个带有一个参数的宏,因此根据 TeX 关于无界参数的规则,
\textbf\expandafter{\name}
将\expandafter
作为参数,并且你将获得与
\textbf{\expandafter}{\name}
而这个动作\expandafter
只是比平时提前\else
或稍微扩大一点:\fi
\ifmmode
\nfss@text
{\bfseries\expandafter}% <--- from #1
\else
\hmode@bgroup
\text@command{\expandafter}% <--- from #1
\bfseries\check@icl\expandafter % <--- from #1
\check@icr
\expandafter\egroup
\fi
(据我所知,\text@command{\expandafter}
不会做任何危险的事情,但也不会做任何有用的事情)。严格来说,\textbf
没有参数,但它的扩展是\protect\textbf•
(项目符号表示名称中的空格)并且它\textbf•
需要一个参数,但这与主题基本无关。
还请注意
\expandafter\uppercase\expandafter{\name}
以及
\expandafter\textbf\expandafter{\name}
但在前者中,第一个\expandafter
是多余的,而在后者中两者都是多余的,因为\textbf
本质上打开了一个组,其中\bfseries
生效并且参数被排版,就像宏扩展一样。是的,\textbf
从上面的代码中你可以看到,它还做了许多其他事情(用 替换前三个\expandafter
标记\name
),但我猜你\textbf
只是举个例子。事实上,同样适用于每一个宏采取一个或多个参数。
答案2
在这种情况下,答案只是“考虑到内部实现,TeXbook 指出它会是”。
首先,\uppercase
是一个原始的 TeX 命令,其语法是\uppercase ⟨general text⟩
。因此,它将扩展输入流中的以下标记,直到找到一些{
(任何具有 bgroup 含义的标记)。(TeXbook 第 24 章或https://tex.stackexchange.com/a/267413/250119)
在这种情况下\expandafter
是必需的,因为否则\uppercase
将应用于标记列表\name
而不是标记列表hello world
,这不会产生将里面的文本大写的预期效果。
也可以看看https://tex.stackexchange.com/a/199783/250119。
在另一种情况下\textbf
定义为......
$ latexdef textbf
\textbf:
macro:->\protect \textbf
\textbf :
\long macro:#1->\ifmmode \nfss@text {\bfseries #1}\else \hmode@bgroup \text@command {#1}\bfseries \check@icl #1\check@icr \expandafter \egroup \fi
鉴于 TeX 抓取未限定参数的规则,\expandafter
将被抓取为参数,并且以下部分不会加粗。
备注:某些 LaTeX 命令的实现可能恰好\command \expandafter { ... }
能够正常工作(例如\exp_not:n
),但除非它作为命令行为的一部分明确记录下来,否则最好不要使用它。
在这种情况下,只需使用\textbf{\name}
即可。替代方法包括\expandafter\textbf\expandafter{\name}
,\ExpandArgs{o}\textbf{\name}
或者\ExpandArgs{v}\textbf\name
如果需要将 的值传递\name
到命令中。