\ pgfmathsetlengthmacro non compatibile con un loop?

Sep 06 2020

Il codice seguente viene compilato perfettamente:

\documentclass[]{article}

\usepackage[most]{tcolorbox}

\newcommand\UseImageLeft[1]{%
\IfFileExists{#1\i.jpg}%
  {\includegraphics[scale=0.2]{#1\i.jpg}}%
    {\includegraphics[scale=0.2]{example-image.jpg}}%
}

\def\ImageLeft{\UseImageLeft{Image-}}

% Problem ===================================
% \pgfmathsetlengthmacro\LeftWidth{width("\ImageLeft")}
% =======================================

\begin{document}

\foreach \i in {1,...,2}{

% On any page (begin)
\begin{tcolorbox}[]
\begin{tcbitemize}[]
\tcbitem[] \ImageLeft
\end{tcbitemize}
\end{tcolorbox}
% On any page (end)

\newpage
}

\end{document}

Il mio problema è che voglio decommentare \pgfmathsetlengthmacro\LeftWidth{width("\ImageLeft")}, ma poi il codice non si compila più. Come posso risolvere questo problema per favore?

Risposte

2 UlrichDiez Sep 06 2020 at 14:31

Problema 1:

Si utilizza \pgfmathsetlengthmacro\LeftWidth{width("\ImageLeft")} al di fuori del \foreach-loop. Ma con \pgfmathsetlengthmacro\LeftWidth{width("\ImageLeft")}la macro \ImageLeftviene utilizzata che a sua volta utilizza \UseImageLeftche a sua volta utilizza \iwhile \iè definita per restituire il numero di un'immagine solo all'interno del \foreachciclo.

Problema 2:

Hai quello \pgfmathsetlengthmacro\LeftWidth{width("\ImageLeft")}che è di modello

\pgfmathsetlengthmacro\LeftWidth{⟨expression⟩}
con
⟨expression⟩ = width("\ImageLeft")
, mentre
width("\ImageLeft")
a sua volta è di pattern con = .
width(⟨expression⟩)

⟨expression⟩"\ImageLeft"

Ciò significa che \ImageLeftviene valutato come parte di ⟨expression⟩due volte:

La prima volta in cui viene valutata la ⟨expression⟩formazione dell'argomento di \pgfmathsetlengthmacro\LeftWidth.

La seconda volta in cui viene valutata la ⟨expression⟩formazione dell'argomento di width(...).

pgfmanual.pdf, sezione "95.2 Sintassi per espressioni matematiche: operatori" dice nella spiegazione dell'operatore-:"x"

... Tuttavia, poiché ogni espressione viene espansa con \ edef prima di essere analizzata, le macro (ad esempio, comandi di font come \ tt o \ Huge) potrebbero dover essere "protette" da questa espansione (ad esempio, \ noexpand \ Huge). Idealmente, dovresti comunque evitare tali macro. Ovviamente, questi operatori dovrebbero essere usati con molta attenzione poiché è improbabile che ulteriori calcoli siano possibili con il risultato.

Questa informazione non è correlata solo all'operatore. Questa informazione è rilevante ogni volta che si tratta di valutare un . È una regola generale che viene applicata durante la valutazione di ogni . Pertanto penso che non sia una buona idea trasmettere queste informazioni solo durante la spiegazione di un operatore speciale."x"⟨expression⟩⟨expression⟩

Ad ogni modo:

Mettendo insieme le informazioni, scopri che \ImageLeftviene valutata tramite \edefdue volte. Poiché la catena di espansione è \ImageLeft\UseImageLeft...\includegraphics..., ciò implica che due volte i tentativi di valutazione di \includegraphicsvia \edefabbiano luogo.
Ma nel caso in cui non usi quella delle più recenti distribuzioni TeX \includegraphicsnon funziona nei \edefcontesti di espansione. Questo perché nelle vecchie distribuzioni TeX \includegraphicsnon è "protetto" - "protetto" si riferisce a un meccanismo interno a LaTeX per prevenire l'espansione dei comandi in situazioni in cui tale espansione non funzionerebbe - e "si basa su" eseguire assegnazioni temporanee, ad esempio in termini di \def, mentre tali incarichi non vengono svolti mentre la \edefvalutazione è in corso. \edefinnesca l'espansione di token espandibili mentre \defè una primitiva non espandibile .

Quindi, se non si utilizza una delle più recenti distribuzioni TeX, è necessario adottare misure precauzionali per impedire l'espansione / valutazione del comando \ImageLeft(e quindi anche impedire l'espansione / valutazione di \includegraphics) con ciascuna di queste \edefvalutazioni. (Nelle distribuzioni TeX più recenti queste misure precauzionali non sono né un fastidio né sono necessarie.)

È possibile impedire l'espansione / valutazione anteponendo \ImageLeftcon \noexpand\noexpand\noexpand.

La prima \edefvalutazione, applicata <code>\noexpand\noexpand\noexpand\ImageLeft</code>,
produce
<code>\noexpand\ImageLeft</code>.
Il secondo \edef-valutazione verrà applicata su quella, che a sua volta produce:
<code>\ImageLeft</code>.

Problema 3:

egreg ha sottolineato che \iè già definito in LaTeX e che probabilmente non si vuole sovrascrivere quella definizione. Pertanto nell'esempio seguente ho usato al \NiceForEachElementposto di \i.

\documentclass[]{article}

\usepackage[most]{tcolorbox}

\newcommand\UseImageLeft[1]{%
\IfFileExists{#1\NiceForEachElement.jpg}%
  {\includegraphics[scale=0.2]{#1\NiceForEachElement.jpg}}%
    {\includegraphics[scale=0.2]{example-image.jpg}}%
}

\def\ImageLeft{\UseImageLeft{Image-}}

% Problem ===================================
%\pgfmathsetlengthmacro\LeftWidth{width("\ImageLeft")}
% =======================================

\begin{document}

\foreach \NiceForEachElement in {1,...,2}{%
\pgfmathsetlengthmacro\LeftWidth{width("\noexpand\noexpand\noexpand\ImageLeft")}%
%Activate the following line in case you wish to see on screen/console what the definition of \LeftWidth looks like now:
%\show\LeftWidth
% On any page (begin)
\begin{tcolorbox}[]%
\begin{tcbitemize}[]%
\tcbitem[] \ImageLeft
\end{tcbitemize}%
\end{tcolorbox}%
% On any page (end)
\newpage
}

\end{document}

Se desideri essere in grado di utilizzare anche \ImageLeftal di fuori del ciclo, definisci \ImageLeftecc. Per elaborare un argomento invece di elaborare \i/ \NiceForEachElement.
All'interno del ciclo puoi passare \i/ \NiceForEachElementcome argomento.
Fuori dal ciclo puoi passare direttamente il numero dell'immagine come argomento.

\documentclass[]{article}

\usepackage[most]{tcolorbox}

% Define the command \NiceForEachElement to ensure error-message in case it is already defined.
% This way you can ensure to a certain degree that using `\niceelement` as Foreach-variable
% does not override something that alerady exists.
\newcommand\NiceForEachElement{}%

\newcommand\UseImageLeft[2]{%
% #1 preceding phrase "image-"
% #2 number of image
\IfFileExists{#1#2.jpg}%
  {\includegraphics[scale=0.2]{#1#2.jpg}}%
    {\includegraphics[scale=0.2]{example-image.jpg}}%
}

\newcommand*\ImageLeft[1]{\UseImageLeft{Image-}{#1}}

% Problem ===================================
%\pgfmathsetlengthmacro\LeftWidth{width("\ImageLeft")}
% =======================================

\begin{document}

% outside the loop the width of Image-7.jpg or example-image.jpg:

\pgfmathsetlengthmacro\LeftWidth{width("\noexpand\noexpand\noexpand\ImageLeft{7}")}%
%Activate the following line in case you wish to see on screen/console what the definition of \LeftWidth looks like now:
%\show\LeftWidth

% inside the loop:

\foreach \NiceForEachElement in {1,...,2}{%
  \pgfmathsetlengthmacro\LeftWidth{width("\noexpand\noexpand\noexpand\ImageLeft{\NiceForEachElement}")}%
  %Activate the following line in case you wish to see on screen/console  what the definition of \LeftWidth looks like now:
  %\show\LeftWidth
  % On any page (begin)
  \begin{tcolorbox}[]%
  \begin{tcbitemize}[]%
  \tcbitem[] \ImageLeft{\NiceForEachElement}%
  \end{tcbitemize}%
  \end{tcolorbox}%
  % On any page (end)
  \newpage
}

\end{document}
2 UlrikeFischer Sep 06 2020 at 13:59

Non utilizzare variabili di ciclo al di fuori di un ciclo, né nel codice eseguito, né nelle definizioni.

\documentclass[]{article}

\usepackage[most]{tcolorbox}

\newcommand\UseImageLeft[1]{%
\IfFileExists{#1.jpg}%
  {\includegraphics[scale=0.2]{#1.jpg}}%
  {\includegraphics[scale=0.2]{example-image.jpg}}%
}


\begin{document}

\foreach \i in {1,...,2}{%
\pgfmathsetlengthmacro\LeftWidth{width("\UseImageLeft{image\i}")}%
% On any page (begin)
\begin{tcolorbox}[]
\begin{tcbitemize}[]
\tcbitem[] \UseImageLeft{image\i}
\end{tcbitemize}
\end{tcolorbox}
% On any page (end)

\newpage
}^

\end{document}