答案1
对 wipet 的好答案的一些补充。
中的定义plain.tex
如下(第575至578行):
\def\line{\hbox to\hsize}
\def\leftline#1{\line{#1\hss}}
\def\rightline#1{\line{\hss#1}}
\def\centerline#1{\line{\hss#1\hss}}
您可能会注意到,这\line
只是的简写\hbox to\hsize
,因此调用
\line{\hss"hello world\the\catcode`\ "\hss}
变成
\hbox to\hsize{\hss"hello world\the\catcode`\ "\hss}
有人可能会认为,这样的调用将遵循与
\centerline{\hss"hello world\the\catcode`\ "\hss}
即被{\hss"hello world\the\catcode`\ "\hss}
吸收作为的论据\hbox
,但这两种情况有着根本的区别。
您应该意识到这一点,因为您正在定义 active"
来执行操作\hbox\bgroup
,而接受参数的宏不允许使用这样的语法来表示参数的开始。在 TeXbook 的第 278 页,您可以找到语法\hbox
:
\hbox
⟨盒子规格⟩{
⟨横模材质⟩}
并且这种表示法意味着括号也可以是隐式分别具有类别代码 1 和 2 的字符。在这些情况下,这些括号内的材料仅在{
处理后才被扫描(这里,也是在材料被插入后\everyhbox
)\afterassignment
。匹配}
将启动将水平列表转换为框的操作。
尤其,
\hbox{\catcode`\ =12 a b}
将执行类别代码分配和该空间将被吸收为类别代码 12。
相反,\centerline{\catcode`\ =12 a b}
将吸收参数并对其进行标记化(如 Petr 所解释的)。当 TeX 吸收参数时,它不执行扩展和赋值;如果参数未分隔(如 的情况\centerline
),则参数将是下一个标记(跳过显式空格标记后),除非此标记是左括号(类别代码为 1 的显式字符),此时参数将是匹配的右括号(类别代码为 2 的显式字符)之前的标记。执行标记化,因此空格的类别代码为 10。
有可能解决这个问题吗?可以,使用 e-TeX。
\catcode`\"=\active
\def"#1"{%
\leavevmode\hbox{%
\catcode`\ =12
\tt
\scantokens{#1\relax}%
}%
}
\line{\hss X"hello world"X\hss}
\centerline{X"hello world"X}
\bye
答案2
\centerline
由 定义\def\centerline#1{\line{\hss#1\hss}}
,但\line
由 定义\def\line{\hbox to\hsize}
。这意味着\line{next text}
不将下一个文本作为参数。下一个文本\hbox
在 TeX 主处理器的原语内部处理。如果有赋值(如\catcode
设置),则会立即完成,然后读取(和标记化)下一个字符。另一方面,\centerline{next text}
首先将下一个文本作为参数读取。在此状态下,所有字符都将被标记化,但尚未完成赋值。读取并标记化参数后,然后在上下文中使用,\hbox...{..next text..}
但此处所有字符都将被标记化。因此,\catcode
设置现在没有效果。当然,只有\the\catcode
报告发生了变化。此报告打印标记处理器对尚未标记化的下一个字符使用的值。