标准(纯 TeX)定义是
\def\leavevmode{\unhbox\voidb@x}
其中\voidb@x
是一个永久空白的框。这将强制 TeX 退出垂直模式,因此被广泛用于启动水平模式材料(特别是在 LaTeX 中)。这个定义对输入处理还有其他影响吗?
答案1
当标准\leavemode
在垂直模式下执行时,\unhbox
原语会将 TeX 切换为水平模式。\unhbox
标记保留在输入流中在那一点上。如果我们安排使用以下方式抓取段落中的第一个标记,则可以看到这一点\everypar
:
\def\temp#1{\def\temp{#1}\show\temp}
\everypar{\temp}
\leavevmode A
\bye
得出
> \temp=macro:
->\unhbox .
如果使用了某种前瞻,这可能会成为一个问题。当然,未装箱的空框对正在构建的垂直列表没有“残留”影响。
标准\leavevmode
是使用 TeX90 的最佳定义。但是,当 e-TeX 可用时,可以使用
\protected\def\leavevmode{\ifvmode\expandafter\indent\fi}
有了这种缩进,TeX 会在插入\indent
之前执行\everypar
。因此输入中不会有任何“杂散”的内容。(尝试上面的演示来查看这一点。)
之所以需要这样做,\protected
是因为 TeX 在 开始时的行为\halign
。在那里,TeX 将扩展正常的宏,但(可能令人惊讶)这\ifvmode
是真的。因此结果将是“错误的”:
\halign{#\cr \leavevmode a\cr a\cr}
\def\leavevmode{\ifvmode\expandafter\indent\fi}
\halign{#\cr \leavevmode a\cr a\cr}
\protected\def\leavevmode{\ifvmode\expandafter\indent\fi}
\halign{#\cr \leavevmode a\cr a\cr}
\bye
e-TeX\protected
机制避免了这种情况,使得\indent
基于的\leavevmode
方法可行。
pdfTeX\quitvmode
作为原语引入,其效果与上面给出的 e-TeX 定义类似。但是,它与 e-TeX 版本并不完全相同:例如,尝试
\protected\def\leavevmode{\ifvmode\expandafter\indent\fi}
$x_\leavevmode a$
$x_\quitvmode b$
\bye
答案2
恐怕我并不完全理解这个“有问有答”的帖子,或者更确切地说,作者发布它时想要表达什么。我最初想在评论中解释我的疑惑,但我发现很难/不可能保持在 600 个字符的限制内,所以我选择了额外的答案;而且我相信,毕竟,这些评论在某种宽泛的意义上确实可以归类为这样的评论。
首先,我认为没有必要指出,⟨水平命令⟩与存储在寄存器中的标记之间的交互\everypar
在第 24 章中有明确而清晰的记录。TeXbook,更准确地说是在第 283 页,其中详细说明了此类命令在垂直模式下的效果:
[…] 如果这些标记中的任何一个在垂直模式或内部垂直模式下作为命令出现,TeX 会自动执行
\indent
上述命令。这将进入水平模式,\everypar
输入中的标记,此后 TeX 将再次看到 ⟨horizontal command⟩。
从上述描述中可以很容易地推断出的行为\leavevmode
——尽管,不可否认,只有知道它的定义才行。
其次,我不确定 Joseph Wright 想要表达什么,他比较了 pdfTeX 的行为\quitvmode
和他\leavevmode
在需要 ⟨math field⟩ 的地方重新定义的 :很明显,可扩展的、重新定义的\leavevmode
消失而没有错误,而\quitvmode
不可扩展且不符合 ⟨math field⟩ 的开头,会导致错误;但我很难将前者的行为视为一个特性,而将后者视为一个错误:谁会想确保垂直模式留在下标的开头?我理解\leavevmode
主要用于宏,因此, 往往会出现在不可预见的地方;但如果这样一个不协调的命令出现在那个位置,发出错误消息不是更明智吗?最后,请注意,Knuth 的\leavevmode
也会导致错误,正如\quitvmode
它一样。
第三,我认为有必要说明一下为什么最好使用 e-TeX 扩展,特别是定义\protected
,以便定义基于\ifvmode
- 的\halign
- 安全版本\leavevmode
:有人可能会想——就像我一样!——在\relax
条件句前面添加的老办法是否不足以满足这一要求。事实上,一旦 TeX 检查(扩展后)a 后面的第一个不可扩展标记 \cr
(包括\cr
结束前导码的)不是 也不是\noalign
,\omit
它就会进入受限水平模式,并开始读取“你-part” 模板的第一列(参见“tex-the-program”的第 768 节)。在这种情况下,答案是,这\relax
在水平模式下使用时会产生偏差,例如,破坏 中的“ff”连字符f\leavevmode f
。再次,人们可能会问这在任何现实世界场景中是否真的很重要,但这一次, a 可能会不可预测地作为较大宏的一部分出现在这样的位置,这一论点\leavevmode
不那么容易被驳斥。(参见 定义背后的理由,例如,\TextOrMath
命令。顺便提一下,请注意,⟨math field⟩ 以 ⟨filler⟩ 开头,\relax
因此不是导致错误,也不会影响输出,就像 这样的构造一样$x_\leavevmode a$
。当然,Knuth 的定义也存在同样的问题,因为\unhbox
也会破坏连字符。
最后,这里有一些代码可以说明上述声明:
% !TEX TS-program = pdftex
\def\test{
{\tt \meaning\leavevmode}
% Abc $x_\leavevmode a$ xxx;
Note: ff vs. f\leavevmode f
\medskip\halign{##\cr \leavevmode a\cr a\cr}\bigskip
\hrule\bigskip
}
%%%%%%%%
% Original definition by Knuth:
\test
\def\leavevmode{\ifvmode\expandafter\indent\fi}
\test
\def\leavevmode{\relax\ifvmode\expandafter\indent\fi}
\test
\protected\def\leavevmode{\ifvmode\expandafter\indent\fi}
\test
% abc $x_\quitvmode b$ yyy.
\bye
当然,使用纯 TeX 进行编译,使用 pdfTeX 作为排版引擎。两个“下标”测试的代码行最初被注释掉,以避免编译过程中出现错误:您可以在空闲时激活它们。