为什么 \expandafter 可以与 \uppercase 一起使用,但是却不能与 \textbf 一起使用?

为什么 \expandafter 可以与 \uppercase 一起使用,但是却不能与 \textbf 一起使用?

为什么 \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到命令中。

相关内容