了解 \addtocontents 中的扩展

了解 \addtocontents 中的扩展

我对 中的扩展方式感到困惑\addtocontents,或者更确切地说是 中的扩展方式\protected@write。据我所知,\addtocontents应该本质上用 扩展其第二个参数\protected@edef并将结果写入辅助文件。根据这种理解,我预计行为(最多\protected@)与 相同

\iow_shipout:Ne \@auxout
  {
    \exp_not:N \@writefile { #1 } { #2 }
  }

但是正如下面的 MWE 所示,当他们试图用 来阻止论证内部扩展时,他们的行为有所不同\exp_not:n

\documentclass{article}

\makeatletter

\ExplSyntaxOn
\NewDocumentCommand { \naiveaddtocontents } { m m }
  {
    \iow_shipout:Ne \@auxout
      {
        \exp_not:N \@writefile { #1 } { #2 }
      }
  }
\ExplSyntaxOff

\def\abc{some text}

\begin{document}

bla

\ExplSyntaxOn

% this does what I expect
\protected@edef \l_tmpa_tl { \exp_not:n { \abc } }
\tl_show:N \l_tmpa_tl

% so does this
\naiveaddtocontents{ lof }{ \exp_not:n { \abc } }

% this does not
\addtocontents{ lof }{ \exp_not:n { \abc } }

\ExplSyntaxOff

\end{document}

辅助文件如下所示

\relax 
\@writefile {lof}{\abc }
\@writefile{lof}{some text}
\gdef \@abspage@last{1}

因此,\naiveaddtocontents使用 可以防止扩展\exp_not:n,但\addtocontents不能。对于我根本不想扩展参数的用例,我可以使用这个\naiveaddtocontents。但为什么 不能\exp_not:n像我错误预期的那样工作\addtocontents

答案1

该命令\addtocontents使用。以下是from\protected@write的定义:\protected@writesource2e

\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
}

相关部分是\edef\reserved@a{\write#1{#3}}%。如果将上述的定义\protected@write添加到文档中,并在上一行中将\edef其替换为,\def则在 .aux 文件中\addtocontents{ lof }{ \exp_not:n { \abc } }给出。\@writefile{lof}{\abc }

然而,随着\edef,会发生额外的扩展,使得.aux 文件中\addtocontents{ lof }{ \exp_not:n { \abc } }出现。\@writefile{lof}{some text}

因此,如果添加\edef附加内容,该示例就可以工作:在 .aux 文件中给出。\exp_not:n\addtocontents{ lof }{ \exp_not:n { \exp_not:n { \abc } } }\@writefile{lof}{\abc }

答案2

让我们看一下的定义\addtocontents

% latex.ltx, line 14289:
\long\def\addtocontents#1#2{%
  \protected@write\@auxout
      {\let\label\@gobble \let\index\@gobble \let\glossary\@gobble}%
      {\string\@writefile{#1}{#2}}}

\label如果在 的第二个参数中有一个 ,那么按照你幼稚的定义,你会遇到大问题\addtocontents。但我们先不考虑这一点,因为这是一个技术问题。

假设你想要\addtocontents{toc}{Hey, this is \textbf{boldface}}

\documentclass{article}

\makeatletter
\ExplSyntaxOn
\NewDocumentCommand { \naiveaddtocontents } { m m }
  {
    \iow_shipout:Ne \@auxout
      {
        \exp_not:N \@writefile { #1 } { #2 }
      }
  }
\ExplSyntaxOff
\makeatother

\begin{document}

Some text

\addtocontents{toc}{Hey, this is \textbf{boldface}}

\naiveaddtocontents{toc}{Hey, this is \textbf{boldface}}

\end{document}

控制台将打印

(\end occurred when \ifx on line 21 was incomplete)
(\end occurred when \ifx on line 21 was incomplete)
(\end occurred when \ifx on line 21 was incomplete)
(\end occurred when \ifmmode on line 21 was incomplete)</usr/local/texlive/2023
/texmf-dist/fonts/type1/public/amsfonts/cm/cmr10.pfb>

并且aux文件将具有

\relax
\@writefile{toc}{Hey, this is \textbf  {boldface}}
\@writefile {toc}{Hey, this is \protect \unhbox \voidb@x \bgroup \edef l3backend-pdftex.def{boldface}\let \futurelet \@let@token \let \protect \relax \edef cmr{cmr}\edef cmss{cmss}\edef cmtt{cmtt}\def ##1,b,{}\series@check@toks {,ulm,elm,lm,slm,mm,sbm,bm,ebm,ubm,muc,mec,mc,msc,msx,mx,mex,mux,{}{},b,}\edef {}\edef b{b}\def ##1,m,{}\series@check@toks {,ulm,elm,lm,slm,mm,sbm,bm,ebm,ubm,muc,mec,mc,msc,msx,mx,mex,mux,{}{},m,}\edef {}\edef m{m}\protect \let }
\gdef \@abspage@last{1}

这可不是你想看到的,对吧?让我们来解决这个问题。

\documentclass{article}

\makeatletter
\ExplSyntaxOn
\NewDocumentCommand { \naiveaddtocontents } { m m }
  {
    \iow_shipout:Ne \@auxout
      {
        \exp_not:N \@writefile { #1 } { \text_expand:n { #2 } }
      }
  }
\ExplSyntaxOff
\makeatother

\newcommand{\abc}{some text}

\begin{document}

Some text

\addtocontents{toc}{Hey, this is \textbf{boldface} and \abc}

\naiveaddtocontents{toc}{Hey, this is \textbf{boldface} and \abc}

\end{document}

现在aux文件将具有

\relax
\@writefile{toc}{Hey, this is \textbf  {boldface} and some text}
\@writefile {toc}{Hey, this is \textbf {boldface} and some text}
\gdef \@abspage@last{1}

基本上,其作用\text_expand:n与 大致相同\protected@edef,但将生成的标记列表包裹在 中\unexpanded

如果你这样做\exp_not:n(即\unexpanded),TeX 将会这样做扩展,所以你得到的\abc不是它的扩展。

不带\makeatletter\makeatother

\ExplSyntaxOn
\NewDocumentCommand { \naiveaddtocontents } { m m }
  {
    \iow_shipout:ce { @auxout }
      {
        \token_to_str:c { @writefile } { #1 } { \text_expand:n { #2 } }
      }
  }
\ExplSyntaxOff

你可以\token_to_str:c使用\exp_not:c

相关内容