一位朋友与我分享了一份 LaTeX 文档,其中基本上使用了以下代码,我确信它无法编译:
\documentclass{amsart}
\begin{document}
\(a_\text b\)
\end{document}
它可以很好地编译,结果与引入更多括号(即\(a_{\text{b}}\)
)的结果相同。我很好奇是什么让它起作用,并尝试了:
\documentclass{amsart}
\begin{document}
\(a_\operatorname b\)
\end{document}
这会产生错误
! Missing { inserted.
<to be read again>
\let
l.3 \(a_\operatorname
b\)
插入括号\(a_\operatorname{b}\)
本质上会产生相同的错误(当然,这样\(a_{\operatorname{b}}\)
做是可行的)。为什么这些宏的行为不同?
答案1
首先,\(a_\text b\)
是错误输入(它确实有效,但无论如何都是错误的)。正确的输入应该是\(a_{\text{b}}\)
,对于这种情况也是一样\operatorname
。一种情况有效,另一种情况无效,这是由于这两个宏的实现细节造成的,不应依赖这些细节(它们不太可能在不久的将来发生变化,但无论如何……)
也就是说,\text
之所以这样工作,是因为(经过三个扩展步骤)TeX 看到的第一个标记是{
,所以它本质上插入了你遗漏的{
。 。的第一个扩展是。这里是(这里被忽略)并扩展为(在检查我们处于数学模式后),并以 开头。}
\text
\protect\text␣␣
\protect
\relax
\text␣␣
\text@
\text@
{
\operatorname
另一方面, 包含一个\@ifstar
测试,它是不可扩展的,因此 TeX 看到的第一个标记(在四个扩展步骤之后)是\let
,它在那里是无效的,因此会引发错误。您可以通过编写 来重现相同的错误\(a_\let\)
。
答案2
假设标准的 catcode 制度以便{
开始一个组并}
结束它,然后......
官方的 LaTex 文档会告诉你要始终用括号括住参数。这样做的部分原因是为了避免必须记录省略括号时会发生什么情况……
宏扫描未限定的参数而不扩展任何标记,如果下一个非空间标记是,{
则取组(的内容),否则将单个标记作为参数。
相反,大多数 TeX 原语(例如_
或)\hbox
会递归扩展以下标记,直到到达不可扩展的标记,然后接受隐式或显式{
标记来启动分组参数。
\documentclass{article}
\newcommand \zzA{1234}
\newcommand \zzB{{1234}}
\newcommand \zzC{\zzca}
\newcommand\zzca{\bgroup 1234}
\begin{document}
1 \fbox\zzA \fbox{\zzA}
2 $X_\zzA \quad X_{\zzA}$
3 \fbox\zzB \fbox{\zzB}
4 $X_\zzB \quad X_{\zzB}$
5 a \fbox\zzC\egroup b\fbox{\zzC \egroup}
6 $X_\zzC} \quad X_{\zzC}}$
\end{document}
因此请注意,在第 1 行中,宏\fbox
将全部 1234 作为参数,\fbox\zzA
但X_\zzA
进行扩展\zzA
,因此仅将 1 作为下标。
第 5 行的行为特别“有趣”,我们可以记录代码中的跟踪并说明为什么在无括号版本中没有出现框,但记录\fbox
(以及所有乳胶命令)应始终与带括号的参数一起使用不太可能吓跑 99% 的用户群。