在尝试学习 LaTeX 中的宏时,我遇到了这个定义:
\x@protect
是一个带有一个参数的宏,用“ ”表示#1
。\@typeset@protect
也被定义为\relax
,因此\ifx
执行 的第一个分支,它不执行任何操作。 (\ifx
比较两个宏的含义。)所以 的唯一结果是它的参数,即 的定义中的\x@protect
第一个“ ”被丢弃。 这样就剩下命令 \protect(一个无操作)和它自己。 这似乎是一个循环定义。 事实上,它不是。 这是 LaTeX 作者试图掩盖其踪迹的卑鄙伎俩。 再仔细看看 的列表,非常可疑。 最后一个“ ”和句点之间有两个空格,而其他列表中的最后一个控制序列后只有一个! 事实上,所有列表中只有一个尾随空格。 列表中的倒数第二个空格是其最后一个控制序列名称的一部分,即“ ”,包括空格!`\\
\\
\\
\\
\\
\\
\\
为什么会出现循环引用?
为什么遇到循环引用它不会崩溃?
答案1
让我们开始一个互动环节pdflatex test
,test.tex
其中
\documentclass{article}
\DeclareRobustCommand{\?}{js bibra}
\makeatletter
\show\?
该\show
命令将停止运行,因此我们可以发出更多命令
This is pdfTeX, Version 3.14159265-2.6-1.40.20 (TeX Live 2019) (preloaded format=pdflatex)
restricted \write18 enabled.
entering extended mode
(./test.tex
LaTeX2e <2019-10-01> patch level 3
(/usr/local/texlive/2019/texmf-dist/tex/latex/base/article.cls
Document Class: article 2019/10/25 v1.4k Standard LaTeX document class
(/usr/local/texlive/2019/texmf-dist/tex/latex/base/size10.clo))
> \?=macro:
->\x@protect \?\protect \? .
l.7 \show\?
? i\show\x@protect
> \x@protect=macro:
#1->\ifx \protect \@typeset@protect \else \@x@protect #1\fi .
<insert> \show\x@protect
l.7 \show\?
? i\show\@x@protect
> \@x@protect=macro:
#1\fi #2#3->\fi \protect #1.
<insert> \show\@x@protect
l.7 \show\?
\?
处理时会发生什么?有两种情况:如果与(即)\protect
的含义不同,则遵循错误分支。因此输入流将具有\@typeset@protect
\relax
\@x@protect\?\fi\protect\?
而的扩展\@x@protect
将删除最后两个标记,剩下\protect\?\fi
(并\fi
最终消失)。
例如,在\protected@edef
或中\protected@write
,当\protect
被赋予与 不同的含义时,就会发生这种情况\@typeset@protect
。
如果有,则条件为真,因此输入流在跳过错误分支后将具有,
\protect\?
现在\protect
消失了,我们似乎和之前一样。但事实并非如此,因为后面跟着一个\protect
token不同的从\?
测试文档中输入。
仔细查看第一个命令的输出\show
。我们得到
->\x@protect \?\protect \? .
在 和 之间的->
句点,TeX 表示宏的替换文本。这种表示的规则是控制字后面有一个空格,而控制符号后面没有空格。这解释了 和 后面为什么有空格,\x@protect
以及\protect
后面为什么没有空格\?
。但是 句点前面有二空格!它们从哪里来的?
当你这样做时\DeclareRobustCommand{\?}{js bibra}
,LaTeX 会做几件事,其中最主要的是
\expandafter\def\csname ? \endcsname{js bibra}
然后使用这个名称非常不标准的宏来定义“用户级版本” 。请注意宏名称中的\?
之前的空格。\endcsname
还有一些细节,但这个想法是为了简化辅助文件的编写。在旧版本的 LaTeX 中,我们看到了类似
\def\LaTeX{\protect\pLaTeX}
\def\pLaTeX{<the real definition>}
当 LaTeX2e 发布时,以前的代码变成了
\DeclareRobustCommand{\LaTeX}{<the real definition>}
利用新的抽象层次。在旧版本中,\LaTeX{}
章节标题应该这样写
\protect\pLaTeX {}
在辅助文件中。现在它写出
\LaTeX {}
因为名称和规则中的尾随空格。读入辅助文件时将忽略双倍空格。
\\
对于控制符号(例如或 )稍有不同\?
,但总体思路是相同的。