
\addtocontents
、より正確には での展開がどのように機能するかについて混乱しています\protected@write
。私の理解では、 は\addtocontents
基本的に 2 番目の引数を で展開し、その結果を aux ファイルに書き込む必要があります\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}
auxファイルは次のようになります
\relax
\@writefile {lof}{\abc }
\@writefile{lof}{some text}
\gdef \@abspage@last{1}
では\naiveaddtocontents
による展開は防止されます\exp_not:n
が、 では\addtocontents
防止されません。引数をまったく展開したくないユースケースでは、 を使用できます。しかし、では が誤って期待したとおりに動作し\naiveaddtocontents
ないのはなぜでしょうか?\exp_not:n
\addtocontents
答え1
コマンドは\addtocontents
を使用します。以下はからの\protected@write
の定義です:\protected@write
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
置き換えられる\def
と、 .aux ファイルでは になり\addtocontents{ lof }{ \exp_not:n { \abc } }
ます。\@writefile{lof}{\abc }
ただし、 では\edef
、追加の展開が発生し、 が.aux ファイルで\addtocontents{ lof }{ \exp_not:n { \abc } }
提供されます。\@writefile{lof}{some text}
したがって\edef
、 では、 を追加すれば例は機能します\exp_not:n
。 は、.aux ファイルに\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
あなたの素朴な定義では、 の 2 番目の引数にがある場合に大きな問題が生じます\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