我有以下代码。显然,这实际上不是有效的代码,因为没有第二个参数\frac
,但尽管如此,我还是很好奇它为什么会产生这样的结果。
\[
\text{abc$\frac{123}$}
\]
如果您在命令中使用内联或显示数学模式,则您只能看到递归行为(获取重复的、较小的 abc-123)\text{}
。更改所使用的数学模式会稍微改变结果,但重复的 abc-123 仍然存在。
如果不包含该\text{}
命令,则会得到“预期”的输出,例如
abc$\frac{123}$
两者
\[ abc\frac{123} \]
都为您提供单个 abc-123
有人知道什么内部运作导致了递归行为吗?
梅威瑟:
\documentclass{article}
\usepackage{amsmath}
\begin{document}
\[
\text{abc$\frac{123}$}
\]
\[
abc\frac{123}
\]
abc$\frac{123}$
\[
\text{abc\[\frac{123}\]}
\]
\end{document}
答案1
为了理解发生了什么,让我们考虑一个稍微简单一点的例子。定义\macro
为一个带有一个参数的宏
\newcommand\macro[1]{abc}
参数被忽略,扩展仅包含abc
。现在我们使用\macro
但\text
省略参数。
\[ \text{$\macro$} \]
结果与您观察到的类似:
为了进一步理解这一点,让我们写出一些类似于\text
底层的东西(当然是彻底简化的)。输出与 相同\text
。
\[ \mathchoice{\hbox{$\displaystyle \macro$}}%
{\hbox{$\textstyle \macro$}}%
{\hbox{$\scriptstyle \macro$}}%
{\hbox{$\scriptscriptstyle \macro$}} \]
这里我们使用了 ,\mathchoice
这是一个原语,它接受四个参数,每个参数代表一种可能的数学样式,由我在 中使用的样式宏指示\hbox
。每个参数都在一个临时框中展开和排版,但只会输出与当前样式相对应的参数。因此,以下代码片段将导致
\[ \mathchoice{D}{T}{S}{SS} \] % -> D
$ \mathchoice{D}{T}{S}{SS} $ % -> T
$ _{\mathchoice{D}{T}{S}{SS}} $ % -> S
$ _{_{\mathchoice{D}{T}{S}{SS}}} $ % -> SS
这是使其正确适应周围数学风格的机制\text
。现在,在我们的缺失参数示例中会发生什么。如果我们展开,\macro
我们得到
\[ \mathchoice{\hbox{$\displaystyle abc}}%
{\hbox{$\textstyle abc}}%
{\hbox{$\scriptstyle abc}}%
{\hbox{$\scriptscriptstyle abc}} \]
哎呀,\macro
吃掉了结尾$
,现在有很多不匹配的$
。现在我们可以通过阅读错误消息来跟踪 TeX 的扫描过程。第一个错误是
! Extra }, or forgotten $.
l.4 \[ \mathchoice{\hbox{$\displaystyle \macro$}
}%
TeX 告诉我们,错误发生在读取}
之后\macro$
,换行符表示。它还告诉我们,它原本期待的是$
,但却遇到了}
。这很合理,因为\macro
刚刚吞下了$
。以下几行也发生了完全相同的情况,TeX 仍在寻找$
永远不会出现的结束符,因此所有内容都记录在 的第一个参数中\mathchoice
。这甚至超出了数学环境的范围,并且只有在$
平衡或到达文档末尾时才会结束。最终,TeX 意识到事情出了问题,并开始插入多个$
和 ,}
直到满足条件为止。然后,它排版 的第一个参数,\mathchoice
其中包含直到无效部分为止的所有内容。你可以用这个制作有趣的东西
\documentclass{article}
\usepackage{xcolor}
\newcommand\macro[1]{abc}
\begin{document}
$ \mathchoice{unused}%
{\hbox{\color{red}$\textstyle \macro$}}%
{scriptstyle}%
{scriptscriptstyle} $
Wait, what is happing?
Why is everything red and in one line?
What happened to the linebreaks?
When is this ever going to end?
\end{document}
这种\frac
情况有点难以解释,因为它里面有一个组,但大致的工作原理是一样的。未终止的宏会吃掉尾随的字符$
,而 TeX 会继续扫描,直到$
达到平衡或到达文档末尾。
答案2
前提是错误的。是代码中的第二个参数\frac
正是$
。
的定义frac
是
\DeclareRobustCommand{\frac}[2]{{\begingroup#1\endgroup\@@over#2}}
其中与重新定义以产生警告的原语相同。由于该命令需要两个参数,因此它可以作为第一个参数进行扫描,\@@over
然后由于没有后续参数,它会吸收下一个参数\over
amsmath
{123}
{
令牌作为第二个参数。因此,您得到的结果是
$\frac{123}{$}
现在这变成了
${\begingroup123\endgroup\@@over$}
这已经会产生
! Missing } inserted.
<inserted text>
}
l.6 $\frac{123}$
?
! Too many }'s.
\frac #1#2->{\begingroup #1\endgroup \@@over #2}
l.6 $\frac{123}$
?
即使不在 内\text
。$
由于 尚未平衡,因此该标记出现在它不应该出现的位置,{
因此 TeX 添加}
以进行恢复;然后它重新扫描$
,这将结束数学模式,并且下一个}
会触发错误,因为它不再与相应的 平衡{
。
情况\text
更糟,因为\text
会产生四个版本的参数,以便根据最终的数学风格只使用其中一个。上面的两个错误将变成八错误消息,但略有不同,因为它们是在 TeX 正在处理时生成的\text
(\text@
是其内部版本,仅供参考):
! Missing } inserted.
<inserted text>
}
l.6 \[\text{$\frac{123}$}
\]
?
! Missing { inserted.
<to be read again>
}
l.6 \[\text{$\frac{123}$}
\]
?
! Missing } inserted.
<inserted text>
}
l.6 \[\text{$\frac{123}$}
\]
?
! Missing { inserted.
<to be read again>
}
l.6 \[\text{$\frac{123}$}
\]
?
! Missing } inserted.
<inserted text>
}
l.6 \[\text{$\frac{123}$}
\]
?
! Missing } inserted.
<inserted text>
}
l.6 \[\text{$\frac{123}$}
\]
?
! Extra }, or forgotten $.
\text@ ...style \ssf@size {\firstchoice@false #1}}
\check@mathfonts }
l.6 \[\text{$\frac{123}$}
\]
?
! Extra }, or forgotten $.
\text@ ...firstchoice@false #1}}\check@mathfonts }
l.6 \[\text{$\frac{123}$}
\]
?
“abc” 后面跟着一个无分母分数的三个变体,这只是错误恢复的副产品,不应该“认真对待”。实际上,按回车键八次就能让 TeX 同步,这相当令人惊讶。