移动参数和 \protect:理解定义

移动参数和 \protect:理解定义

我试图理解\protect第 11.4 节开头描述的精确语义强大的命令和保护source2e文档,LaTeX2e 格式的注释源代码,第 42-43 页(我已将下面的数字替换为 source2e 中的项目符号):

[...] 在(至少)三种不同情况下,这些命令 [即LaTeX命令 --Evan Aad] 是不安全的。这些被称为“移动参数” LaTeX,包括:

  1. 将信息写入文件,例如索引或目录。
  2. 将信息写入屏幕。
  3. \edef\message\mark或其他命令中,它会充分评估其参数。

LaTeX使脆弱的命令变得健壮的方法是在它们前面加上\protect。这可以有以下五个可能的值之一:

  1. \relax,进行正常排版。因此\protect\foo将执行\foo
  2. \string,用于写入屏幕。因此\protect\foo将写入\foo
  3. \noexpand,用于写入文件。因此\protect\foo写入\foo后会跟一个空格。
  4. \@unexpandable@protect,用于将移动参数写入文件。因此\protect\foo将写入\protect\foo后跟空格。此值也在\edefs、\marks 和其他完全评估其参数的命令中使用。
  5. \@unexpandable@noexpand,用于在 内执行延迟写入\edef。因此\protect\foo将写入\foo后跟一个空格。如果您想要\protect\foo写入,则应使用 \@unexpandable@protect。(已删除,因为从未使用过。)

我正努力解决以下问题。

  1. 第 4 条:术语“移动参数”的使用方式与之前的定义方式不一致。在开头段落中,它被定义为使用命令存在风险的三种情况之一,而在第 4 条中,它表示写入文件的输入块。那么,到底什么是移动参数?移动论点
  2. 第 4 项:第 3 项和第 4 项不匹配:第 3 项说“用于写入文件”,而第 4 项说“用于将移动参数写入文件”。因此,第 3 项中表达的条件涵盖了第 4 项中表达的条件,但每种情况下指定的操作都不同。这怎么可能呢?
  3. 第 5 项:什么是“延迟写入”?
  4. 第 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,因此没有空格。(请记住,\citeTeX 在低级别删除了后面的“空格”。)

对于第三种情况,我们可以再次通过写入文件来“强制解决问题”

\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,生活变得轻松多了。

相关内容