了解 \addtocontents 中的擴展

了解 \addtocontents 中的擴展

我對擴展如何工作\addtocontents,或者更準確地說,如何工作感到困惑\protected@write。據我了解,\addtocontents本質上應該擴展它的第二個參數並將\protected@edef結果寫入 aux 檔案。有了這種理解,我希望行為(最多\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}

aux 檔案看起來像

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

\naiveaddtocontents防止擴展也是如此\exp_not:n,但\addtocontents不是。對於我根本不想擴展參數的用例,我可以使用 this \naiveaddtocontents。但為什麼不能\exp_not:n按照我錯誤的預期工作\addtocontents呢?

答案1

該指令\addtocontents使用\protected@write.以下是\protected@writefrom的定義source2e

\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替換為.aux 檔案中的then \def\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

相關內容