我试图理解\protect
第 11.4 节开头描述的精确语义强大的命令和保护的source2e
文档,LaTeX2e 格式的注释源代码,第 42-43 页(我已将下面的数字替换为 source2e 中的项目符号):
[...] 在(至少)三种不同情况下,这些命令 [即
LaTeX
命令 --Evan Aad] 是不安全的。这些被称为“移动参数”LaTeX
,包括:
- 将信息写入文件,例如索引或目录。
- 将信息写入屏幕。
- 在
\edef
、\message
、\mark
或其他命令中,它会充分评估其参数。
LaTeX
使脆弱的命令变得健壮的方法是在它们前面加上\protect
。这可以有以下五个可能的值之一:
\relax
,进行正常排版。因此\protect\foo
将执行\foo
。\string
,用于写入屏幕。因此\protect\foo
将写入\foo
。\noexpand
,用于写入文件。因此\protect\foo
写入\foo
后会跟一个空格。\@unexpandable@protect
,用于将移动参数写入文件。因此\protect\foo
将写入\protect\foo
后跟空格。此值也在\edef
s、\mark
s 和其他完全评估其参数的命令中使用。\@unexpandable@noexpand
,用于在 内执行延迟写入\edef
。因此\protect\foo
将写入\foo
后跟一个空格。如果您想要\protect\foo
写入,则应使用\@unexpandable@protect
。(已删除,因为从未使用过。)
我正努力解决以下问题。
- 第 4 条:术语“移动参数”的使用方式与之前的定义方式不一致。在开头段落中,它被定义为使用命令存在风险的三种情况之一,而在第 4 条中,它表示写入文件的输入块。那么,到底什么是移动参数?移动论点?
- 第 4 项:第 3 项和第 4 项不匹配:第 3 项说“用于写入文件”,而第 4 项说“用于将移动参数写入文件”。因此,第 3 项中表达的条件涵盖了第 4 项中表达的条件,但每种情况下指定的操作都不同。这怎么可能呢?
- 第 5 项:什么是“延迟写入”?
- 第 5 项:在项目的末尾有一条注释,写着“因从未使用而被删除。”这是什么意思?
这个问题与这个。
答案1
“移动”参数是指某些 LaTeX 命令的参数将被“移动”,要么将其写入文件,要么将其写入消息,要么通过其他方式完全展开它。混淆可能发生,因为这些有一些重叠,并且两种情况可能同时适用。特别是,我们有时需要将材料写入文件,本身读回时将构成一个移动上下文,这就是“写入文件”和“将移动参数写入文件”之间存在差异的原因。我们还可能需要“强制”扩展稍后将再次扩展的内容:“延迟写入”业务。
移动参数的“经典”示例是与分段命令一起使用的强制值:
\section{This-is-a-moving-argument~\cite{Lamport}}
我还特意列出了几个需要妥善处理的项目,~
以及\cite
。两者都具有“强大”的内部结构:
~
=>\nobreakspace {}
=>\protect \nobreakspace[space]
\cite
=>\protect\cite[space]
(这些[space]
标记是令牌名称的一部分,因此很重要。)
让我们看看在各种情况下会发生什么。第一种很简单:排版章节标题。这里,\protect
和\relax
不执行任何操作,我们分别执行\nobreakspace[space]
或\cite[space]
。
为了获得第二种情况,我们需要创建一条消息。我将使用两条消息来说明一个微妙的要点:
\PackageWarning{foo}{\cite seen}
\PackageWarning{foo}{\protect\cite seen}
会产生
Package foo Warning: \cite seen on input line ...
Package foo Warning: \citeseen on input line ...
注意间距差异。应用后,\tracingall
您会看到它像在第一种情况下一样发生,我们将\cite
扩展为\protect\cite[space]
,而后者被\protect
应用\string
到。在第二行中,我们直接将\protect
(\string
)应用于\cite
,因此没有空格。(请记住,\cite
TeX 在低级别删除了后面的“空格”。)
对于第三种情况,我们可以再次通过写入文件来“强制解决问题”
\makeatletter
\newwrite\mywrite
\immediate\openout\mywrite=\jobname.tmp
\protected@write\mywrite{}{\cite{}}
Foo\newpage % See later!
将产生
\cite {}
哪里有二文件中的空格:这是因为我们正在探索案例 3。应用\noexpand
到\cite[space]
插入一个额外的名字后面的空格。(至关重要的是,读回时会忽略这些空格:这是此“技巧”的核心部分。)
在讨论案例 4 之前,我们需要了解\write
“延迟写入”的工作原理和含义。TeX\write
基元在页面输出期间存储其写入参数:这对于了解页码等内容是必要的。扩展发生在写入文件时。这意味着某些内容可能已更改:并不理想。LaTeX\protected@write
宏的工作原理是扩展其参数(有一些例外),然后等待 shipout 处理那些“剩余内容”,其中包括页码:
\long\def \protected@write#1#2#3{%
\begingroup
\let\thepage\relax
#2%
\let\protect\@unexpandable@protect
\edef\reserved@a{\write#1{#3}}%
\reserved@a
\endgroup
\if@nobreak\ifvmode\nobreak\fi\fi
}
这让我们回到案例四和示例\section
。对于写入 TOC 数据,这将最终调用
\addtocontents{toc}{\protect \contentsline {section}
{\ifnum 1>\c@secnumdepth \else \protect \numberline {\csname thesection\endcsname }\fi This-is-a-moving-argument~\cite {Lamport}}
{\thepage }}
然后传递给\protected@write
。“有效载荷”被传递给\edef
,我们被\reserved@a
定义为
\write \@auxout {\@writefile{toc}{\protect \contentsline {section
}{\protect \numberline {1}This-is-a-moving-argument\protect \nobreakspace {}\protect \cite {Lamport}}{\thepage }}}
因为\protect
是\@unexpandable@protect
。现在转到 'raw' \write
,它将使用 进行另一次详尽的扩展\protect
,\noexpand
我们想要的内容将传输到.aux
文件中,包括扩展的页码。
正如所指出的,案例五未被使用,因此被删除!
值得注意的是,所有这些巧妙的设计都早于 e-TeX:今天人们可以创建宏\protected
,而且它们不会在\write
或内扩展\edef
,生活变得轻松多了。