所以,我正在读texdoc tamethebeast
(驯服 BeaST:BibTeX 的 B 到 X) 时,我偶然发现了一个我从未见过的宏,它讨论了的实现\thebibliography
;这就是宏\@mkboth
。 这个答案针对这个问题“\@mkboth 和 \markboth 起什么作用?”解释了它的作用,但后来我发现其他宏内那命令的定义,我已经还从未见过;那宏是\unrestored@protected@xdef
。无论是LaTeX2e 非官方参考手册(2017 年 8 月)也不此网页列表记录TeX 基本控制序列包含任何关于其功能的提及。这让我想知道它的作用是什么,所以有人能回答这个问题和/或给我指出一个参考资料吗?做解释一下它是如何工作的?
答案1
该命令是关于使用扩展定义宏的。TeX 原语\edef
定义一个宏\def
,但它会扩展定义文本。例如:
\def\Who{World}
\edef\Hello{Hello \Who}
该宏\Who
被展开,并且的定义文本\Hello
为Hello World
。
\xdef
只是\edef
:\xdef
=的全局变体\global\edef
。
\edef
在许多情况下,如果扩展了脆弱的宏,则或的硬扩展\xdef
将失败。例如,以下宏不健壮:
\def\FragileMacro{%
\def\MyMacro{...}%
...
}
如果在或\FragileMacro
中使用,则不是可扩展标记,只是保留而不进行进一步处理,即它不定义任何内容。然后尝试扩展 。如果它未定义,则会抛出由于命令未定义而导致的错误消息。或者,它会扩展为一些非活动字符。然后,稍后的 执行将失败,因为无法定义非活动字符。\edef
\xdef
\def
\MyMacro
\FragileMacro
\def
LaTeX 的方式是保护:
\protect
在脆弱的宏阻止其扩展之前。- 可以通过 定义宏为健壮命令
\DeclareRobustCommand
。它会偷偷地\protect
将包含可能脆弱的命令文本的内部宏放进去。
有几个有问题的上下文,例如在 .log 文件/控制台中显示某些内容、写入文件,或者在这种情况下扩展\edef
或\xdef
。 的正确定义取决于这些上下文。在或 的\protect
情况下,它是:\edef
\xdef
\@unexpandable@protect
\def\@unexpandable@protect{\noexpand\protect\noexpand}
然后
\let\protect\@unexpandable@protect
\edef\FooBar{\protect\FragileCommand}
在第一次扩展\protect
为
\edef\FooBar{\noexpand\protect\noexpand\FragileCommand}
\noexpand
阻止下一个标记的扩展,并且定义相当于
\def\FooBar{\protect\FragileCommand}
\protect
被保留并\FragileCommand
阻止其扩展。
内部 LaTeX 宏\@protected@edef
和\@protected@xdef
是纯 TeX\edef
和的LaTeX 对应项\xdef
:
\def\protected@edef{%
\let\@@protect\protect
\let\protect\@unexpandable@protect
\afterassignment\restore@protect
\edef
}
\def\protected@xdef{%
\let\@@protect\protect
\let\protect\@unexpandable@protect
\afterassignment\restore@protect
\xdef
}
首先\@@protect
获取 的当前含义\protect
。然后\protect
根据上下文重新定义(扩展定义)。\protect
获取 的含义\@unexpandable@protect
。 的重新定义\protect
由 恢复\afterassignment
。它\restore@protect
在下一个赋值\edef
或之后立即调用\xdef
。使用先前定义的\restore@protect
恢复 的定义:\protect
\@@protect
\def\restore@protect{\let\protect\@@protect}
该版本\unrestored@protected@xdef
是的优化版本\@protected@edef
,仍然重新定义\protect
,但省略了的恢复\protect
:
\def\unrestored@protected@xdef{%
\let\protect\@unexpandable@protect
\xdef
}
宏定义更小更快,避免了宏扩展和两次赋值。但是,它只能在特殊情况下使用,通常在组的末尾:
\begingroup
...
\@unrestored@protected@xdef\FooBar{\protect\FragileMacro}%
\endgroup
在组结束时,任何本地更改(此处为 的重新定义)都会被恢复。无需\protect
显式恢复。\protect
典型用例latex.ltx
:
\def\@xfootnote[#1]{%
\begingroup
\csname c@\@mpfn\endcsname #1\relax
\unrestored@protected@xdef\@thefnmark{\thempfn}%
\endgroup
\@footnotemark\@footnotetext
}
\def\@xfootnotemark[#1]{%
\begingroup
\c@footnote #1\relax
\unrestored@protected@xdef\@thefnmark{\thefootnote}%
\endgroup
\@footnotemark
}
\def\@xfootnotenext[#1]{%
\begingroup
\csname c@\@mpfn\endcsname #1\relax
\unrestored@protected@xdef\@thefnmark{\thempfn}%
\endgroup
\@footnotetext
}
该小组还出现在\markboth
:
\def\markboth#1#2{%
\begingroup
\let\label\relax \let\index\relax \let\glossary\relax
\unrestored@protected@xdef\@themark {{#1}{#2}}%
\@temptokena \expandafter{\@themark}%
\mark{\the\@temptokena}%
\endgroup
\if@nobreak\ifvmode\nobreak\fi\fi
}
该小组似乎缺少以下内容\@markright
:
\def\@markright#1#2#3{\@temptokena {#1}%
\unrestored@protected@xdef\@themark{{\the\@temptokena}{#3}}%
}
但是\@markright
它本身在一个组内被调用:
\def\markright#1{%
\begingroup
\let\label\relax \let\index\relax \let\glossary\relax
\expandafter\@markright\@themark {#1}%
\@temptokena \expandafter{\@themark}%
\mark{\the\@temptokena}%
\endgroup
\if@nobreak\ifvmode\nobreak\fi\fi
}
\@themark
在定义 之后\@markright
,需要将标记寄存器的新内容添加到\mark
。但是,\mark
再次扩展参数(\@themark
已经被 扩展)。因此,使用\@unrestored@protected@xdef
临时令牌寄存器作为技巧。在令牌寄存器仅扩展令牌寄存器一次之前,内容不会进一步扩展。\@temptokena
\the