
Ich bin verwirrt darüber, wie die Erweiterung in funktioniert \addtocontents
, oder genauer gesagt in \protected@write
. So wie ich es verstehe, \addtocontents
sollte im Wesentlichen sein zweites Argument mit erweitern \protected@edef
und das Ergebnis in die aux-Datei schreiben. Mit diesem Verständnis würde ich erwarten, dass das Verhalten (bis auf einen gewissen Grad \protected@
) dasselbe ist wie
\iow_shipout:Ne \@auxout
{
\exp_not:N \@writefile { #1 } { #2 }
}
Aber wie das folgende MWE zeigt, verhalten sie sich unterschiedlich, wenn sie versuchen, eine Expansion innerhalb ihres Arguments mit zu verhindern \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}
Die AUX-Datei sieht aus wie
\relax
\@writefile {lof}{\abc }
\@writefile{lof}{some text}
\gdef \@abspage@last{1}
\naiveaddtocontents
Das Verhindern der Erweiterung mit ist genauso \exp_not:n
, aber \addtocontents
nicht. Für meinen Anwendungsfall, in dem ich das Argument überhaupt nicht erweitern möchte, kann ich einfach dies verwenden \naiveaddtocontents
. Aber warum funktioniert es nicht \exp_not:n
wie fälschlicherweise erwartet in \addtocontents
?
Antwort1
Der Befehl \addtocontents
verwendet \protected@write
. Unten finden Sie die Definition von \protected@write
from 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
}
Der relevante Teil ist \edef\reserved@a{\write#1{#3}}%
. Wenn die obige Definition von \protected@write
im Dokument hinzugefügt und in der vorherigen Zeile \edef
durch ersetzt würde , ergibt sich \def
dies in der .aux-Datei.\addtocontents{ lof }{ \exp_not:n { \abc } }
\@writefile{lof}{\abc }
Allerdings \edef
erfolgt bei eine zusätzliche Erweiterung, sodass die .aux-Datei \addtocontents{ lof }{ \exp_not:n { \abc } }
entsteht .\@writefile{lof}{some text}
Daher funktioniert das Beispiel mit \edef
, wenn zusätzlich Folgendes in der .aux-Datei \exp_not:n
hinzugefügt wird :\addtocontents{ lof }{ \exp_not:n { \exp_not:n { \abc } } }
\@writefile{lof}{\abc }
Antwort2
Schauen wir uns die Definition von an \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}}}
Mit deiner naiven Definition hättest du große Probleme, wenn du \label
im zweiten Argument ein hast \addtocontents
. Aber lass das mal außen vor, es ist eine Formalität.
Angenommen, Sie möchten \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}
Die Konsole druckt
(\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>
und die aux
Datei wird
\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}
Das ist nicht wirklich das, was Sie sehen möchten, oder? Lassen Sie uns das Problem beheben.
\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}
Die aux
Datei hat nun
\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}
Im Grunde genommen \text_expand:n
macht es ungefähr dasselbe wie \protected@edef
, lässt die resultierende Token-Liste jedoch in eingeschlossen \unexpanded
.
Wenn Sie tun\exp_not:n
(das heißt, \unexpanded
), wird TeXNEINErweiterung, also erhalten Sie \abc
und nicht seine Erweiterung.
Ohne \makeatletter
und \makeatother
:
\ExplSyntaxOn
\NewDocumentCommand { \naiveaddtocontents } { m m }
{
\iow_shipout:ce { @auxout }
{
\token_to_str:c { @writefile } { #1 } { \text_expand:n { #2 } }
}
}
\ExplSyntaxOff
Stattdessen \token_to_str:c
können Sie verwenden\exp_not:c