把转义字符‘\’的catcode改掉后,为什么这段代码还能编译成功?

把转义字符‘\’的catcode改掉后,为什么这段代码还能编译成功?

manmac.tex当我读到这段代码片段时,我读了一些宏

% (now Appendix E resumes again)
% macros for verbatim scanning
\chardef\other=12
\def\ttverbatim{\begingroup
  \catcode`\\=\other
  \catcode`\{=\other
  \catcode`\}=\other
  \catcode`\$=\other
  \catcode`\&=\other
  \catcode`\#=\other
  \catcode`\%=\other
  \catcode`\~=\other
  \catcode`\_=\other
  \catcode`\^=\other
  \obeyspaces \obeylines \tt}

\outer\def\begintt{$$\let\par=\endgraf \ttverbatim \parskip=\z@
  \catcode`\|=0 \rightskip-5pc \ttfinish}
{\catcode`\|=0 |catcode`|\=\other % | is temporary escape character
  |obeylines % end of line is active
  |gdef|ttfinish#1^^M#2\endtt{#1|vbox{#2}|endgroup$$}}

在 的定义中\begintt, 的 catcode\被 改为 12 \ttverbatim,然后是一系列宏\parskip=\z@ \catcode`\|=0 \rightskip-5pc \ttfinish。为了清楚地描述我的问题,我在下面的代码片段中提取了作者的想法:

\def\x{x}
\def\myverbatim{\begingroup\catcode`\\=12 \tt \x }
{\catcode`\^^@=0 ^^@myverbatim ^^@endgroup}

这个输出是x。让我感到疑惑的是,既然 的 catcode\从 0 变成了 12,为什么后面的输入文本\tt \x还能被解析成控制序列 token 呢?我期望的输出是\tt \x

答案1

因为当你定义 时\def\myverbatim{\begingroup\catcode`\\=12 \tt \x }, 的 catcode\是 0,所以 TeX 已经将 和 标记\tt\x控制序列标记,并且 TeX 永远不会在标记被标记之后更改标记。也许这个例子可以帮助你更清楚地看到区别:

\def\x{x}
\def\myverbatim{\begingroup\catcode`\\=12 (1: \tt \x)}
\def\endmyverbatim{\endgroup}
\catcode`|=0

\myverbatim % (1: \tt \x)

(2:\tt \x)
|endmyverbatim

\bye

对于1,TeX 已经将其扫描\tt \x为控制序列标记,当您展开 时\myverbatim,它们仍然是控制序列标记并进行相应的处理。

完全处理 后\myverbatim, 的 catcode\会发生变化,然后 TeX 会继续扫描(2:...。此时,TeX 将 视为\普通字符,因此\tt \x只是一个没有特殊含义的 6 字符字符串。因此,您现在必须键入内容|endmyverbatim才能使环境正确结束(\endmyverbatim只会被排版)。


这个过程就是 Knuth 所说的 TeX 的眼睛和 TeX 的嘴巴。眼睛看着人物并根据当前的 catcode 设置为每个字符分配一个含义。这个含义固定到看到的每个字符上,字符 + 含义构成一个标记。代币然后到达嘴里,根据其含义进行处理。

这个标记不会改变,这就是\makeatletter...\makeatother在定义中不起作用的原因,也是导致逐字环境在另一个宏的参数中使用时不起作用的原因。

答案2

当 TeX 吸收要定义的宏的替换文本时,\def或者\gdef无论对输入作何解释,它都只会根据\def找到时有效的类别代码对其进行标记。使用\edef\xdef仅执行宏扩展。

在这两种情况下,都不会执行任何分配,因此尝试是没有用的\edef\myverbatim

按照你的定义,替换文本\myverbatim

|begingroup| |catcode| `12 |\| =12 112 212 |tt| |x|

其中|...|表示一个符号标记(空格只是为了清楚起见)。

这样做\edef只会产生一个奇怪的错误,因为唯一可扩展的标记是\\,在纯 TeX 中是

\\:
macro:#1pt->#1

(由于历史原因)。

仅有的您调用的\myverbatim替换文本被插入到输入流中,标记被扩展,分配被执行,因此的类别代码\将变成 12。

为什么会这样?一个典型的任务是\advance\mycount by 1,您希望 TeX 在调用时执行此操作,而不是在定义最终执行此操作的宏时执行此操作。同样,如果您在替换文本中有一个宏,您希望在调用时使用它的含义,而不是在定义时使用它的含义。

同样,您希望定义时有效的类别代码与调用时有效的类别代码相同,否则会产生非常奇怪的效果。例如,定义\begintthas $$,您希望这些是总是 $3,而不是在通话时可能发生的任何情况。

相关内容