为什么这种保护不起作用?

为什么这种保护不起作用?

答案问题详细解释了什么是保护,所以我对此并不感到疑惑。我的问题是为什么某个命令定义expl3没有给出受保护的命令。

考虑这个 LaTeX2e 示例:

\documentclass{article}

\DeclareRobustCommand\foo[1]{#1}
\DeclareRobustCommand\?{??}
\expandafter\DeclareRobustCommand\expandafter\-\expandafter{\-}

\begin{document}

\tableofcontents
% this is <jobname>.toc:
% \contentsline {section}{\numberline {1}\foo {bar}}{1}
% \contentsline {section}{\numberline {2}foo\?}{1}
% \contentsline {section}{\numberline {3}foo\-bar}{1}

\section{\foo{bar}}

\section{foo\?}

\section{foo\-bar}

\end{document}

一切如预期般进展。

但是,当我尝试对未受保护的对象expl3执行相同操作时:\-

\documentclass{article}

\usepackage{expl3}
\ExplSyntaxOn
\cs_new_protected:Npn \foo #1 { #1 }          % works
\cs_new_protected:Npn \? { ?? }               % works
\exp_args:NNo \cs_set_protected:Npn \- { \- } % does not work
\ExplSyntaxOff

\begin{document}

\tableofcontents
% this is <jobname>.toc:
% \contentsline {section}{\numberline {1}\foo {bar}}{1}
% \contentsline {section}{\numberline {2}foo\?}{1}
% \contentsline {section}{\numberline {3}foo\discretionary {-}{}{}bar}{1}
% \contentsline {section}{\numberline {4}foo\-bar}{1}

\section{\foo{bar}}

\section{foo\?}

\section{foo\-bar}

\section{foo\protect\-bar}

\end{document}

我做错了什么吗?为什么没有\exp_args:NNo \cs_set_protected:Npn \- { \- }得到想要的结果?我能找到这种行为的唯一命令是\-。是我看得不够仔细还是有什么特别之处\-

答案1

你被 LaTeX2e 而不是 LaTeX2e 所困扰expl3!你使用的重新定义是正确的,如果你这样做,你就会明白这一点\show\-

> \-=\protected\long macro:
->\discretionary {-}{}{}.

然而,LaTeX2e\-在输出例程中重新定义,这是.toc创建条目的地方:

\@arrayparboxrestore ->\let \if@nobreak \iffalse \let \if@noskipsec \iffalse \l
et \par \@@par \let \-\@dischyph \let \'\@acci \let \`\@accii \let \=\@acciii \
parindent \z@ \parskip \z@skip \everypar {}\linewidth \hsize \@totalleftmargin 
\z@ \leftskip \z@skip \rightskip \z@skip \@rightskip \z@skip \parfillskip \@flu
shglue \lineskip \normallineskip \baselineskip \normalbaselineskip \sloppy 
{\let}
{reassigning \if@nobreak=\iffalse}
{\let}
{reassigning \if@noskipsec=\iffalse}
{\let}
{reassigning \par=\par}
{\let}
{changing \-=\protected\long macro:->\discretionary {-}{}{}}
{into \-=macro:->\discretionary {-}{}{}}
...

因此,您可能想知道为什么我们看到受保护命令和 LaTeX2e“强大”命令的行为之间存在差异:毕竟,输出例程都忽略了这两个命令!这取决于不同的保护如何工作。使用 LaTeX3 方法,这取决于引擎,因此每次\edef应用或类似命令时\-,它都保持不变。但是,LaTeX2e 会删除保护。

另一方面,LaTeX2e 的强健机制会导致内部\-变成。这意味着,即使我们稍后更改 的定义,保护仍然“有效”。\protect\-\edef\-

.toc您可以看到 TeX 向when写入的内容\tracingall。借助 LaTeX2e 的强大机制:

\write->\@writefile{toc}{\protect \contentsline {section}{\protect \numberline 
{1}foo\protect \-bar}{\thepage }}

但使用 LaTeX3 的原生引擎系统

\write->\@writefile{toc}{\protect \contentsline {section}{\protect \numberline 
{1}foo\-bar}{\thepage }}

正如 egreg 已经评论的那样,目前最好的解决方案是\@dischyph通过将 engine-protected 设置为 来使其相等\-。 (当然,从长远来看,LaTeX3 格式将使所有内容受到保护或可扩展,这种问题应该会消失。)


顺便说一下,我会用

\cs_set_protected_nopar:Npx \- { \exp_not:o { \- } }

答案2

LaTeX 在输出过程中会执行一些操作\@arrayparboxrestore,其中包括:

\-\@dischyph

其中\@dischyph,由内核定义为

560 \def\-{\discretionary{-}{}{}}
561 \let\@dischyph=\-

(我留下了行号)。这意味着当文件.aux被写出时,\-它再次具有重新定义之前的值(当然不受保护)。\DeclareRobustCommand传递给.aux文件的内容是不是 \-,但有所不同(事实上\protect\-,由于\protect\noexpand,所以变为未展开的\-)。因此问题不明显。

解决方案:添加

\makeatletter
\let\@dischyph\-
\makeatother

重新定义之后\-

相关内容