\ pgfmathsetlengthmacro incompatible con un bucle?

Sep 06 2020

El siguiente código se compila perfectamente:

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

Mi problema es que quiero descomentar \pgfmathsetlengthmacro\LeftWidth{width("\ImageLeft")}, pero luego el código ya no se compila. ¿Cómo puedo solucionar esto por favor?

Respuestas

2 UlrichDiez Sep 06 2020 at 14:31

Problema 1:

Se usa \pgfmathsetlengthmacro\LeftWidth{width("\ImageLeft")} fuera del \foreachbucle. Pero con \pgfmathsetlengthmacro\LeftWidth{width("\ImageLeft")}la macro \ImageLeftse usa que a su vez usa \UseImageLeftque a su vez usa \iwhile \ise define para producir el número de una imagen solo dentro del \foreachbucle.

Problema 2:

Tienes \pgfmathsetlengthmacro\LeftWidth{width("\ImageLeft")}cual es de patron

\pgfmathsetlengthmacro\LeftWidth{⟨expression⟩}
con
⟨expression⟩= width("\ImageLeft")
, mientras que
width("\ImageLeft")
a su vez es de patrón con = .
width(⟨expression⟩)

⟨expression⟩"\ImageLeft"

Esto significa que \ImageLeftse evalúa como parte de ⟨expression⟩dos veces:

La primera vez que se evalúa la ⟨expression⟩formación del argumento de \pgfmathsetlengthmacro\LeftWidth.

La segunda vez que se evalúa la ⟨expression⟩formación del argumento de width(...).

pgfmanual.pdf, sección "95.2 Sintaxis para Expresiones Matemáticas: Operadores" dice en la explicación del -operador:"x"

... Sin embargo, como cada expresión se expande con \ edef antes de analizarla, es posible que las macros (p. Ej., Comandos de fuentes como \ tt o \ Huge) deban "protegerse" de esta expansión (p. Ej., \ Noexpand \ Huge). Idealmente, debería evitar tales macros de todos modos. Obviamente, estos operadores deben usarse con mucho cuidado ya que es poco probable que se puedan realizar más cálculos con el resultado.

Esta información no está relacionada solo con el operador. Esta información es relevante cuando se trata de evaluar un . Es una regla general que se aplica durante la evaluación de cada . Por lo tanto, creo que no es una buena idea transmitir esta información solo mientras se explica un operador especial."x"⟨expression⟩⟨expression⟩

Sea como fuere:

Al juntar las piezas de información, encontrará que \ImageLeftse evalúa mediante \edefdos veces. Como la cadena de expansión es \ImageLeft\UseImageLeft...\includegraphics..., esto implica que se realizan dos intentos de evaluación de \includegraphicsvía \edef.
Pero en caso de que no use una de las distribuciones TeX más recientes \includegraphics, no funciona en \edef-expansion-contexts. Esto se debe a que en las distribuciones TeX más antiguas \includegraphicsno está "protegido" - "protegido" se refiere a un mecanismo interno de LaTeX para prevenir la expansión de comandos en situaciones donde esa expansión no funcionaría - y "depende de" realizar asignaciones temporales, por ejemplo, en términos de \def, mientras tales asignaciones no se lleven a cabo mientras la \edefevaluación está en curso. \edefdesencadena la expansión de tokens expandibles mientras que \defes una primitiva no expandible .

Entonces, si no está utilizando una de las distribuciones TeX más recientes, debe tomar medidas de precaución para evitar expandir / evaluar el comando \ImageLeft(y por lo tanto también evitar la expansión / evaluación de \includegraphics) con cada una de estas \edefevaluaciones. (En las distribuciones TeX más recientes, estas medidas de precaución no son una molestia ni son necesarias).

Puede prevenir la expansión / evaluación mediante un prefijo \ImageLeftcon \noexpand\noexpand\noexpand.

La primera \edefevaluación, aplicada <code>\noexpand\noexpand\noexpand\ImageLeft</code>,
rinde
<code>\noexpand\ImageLeft</code>.
El segundo \edef-Evaluación se aplicará en que, que a su vez produce:
<code>\ImageLeft</code>.

Problema 3:

egreg señaló que \iya está definido en LaTeX y que probablemente no desee anular esa definición. Por lo tanto, en el siguiente ejemplo utilicé en \NiceForEachElementlugar de \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}

Si desea poder usar \ImageLeftfuera del bucle también, defina \ImageLeftetc para procesar un argumento en lugar de procesar \i/ \NiceForEachElement.
Dentro del bucle puede pasar \i/ \NiceForEachElementcomo argumento.
Fuera del ciclo, puede pasar el número de la imagen como argumento directamente.

\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

No use variables de ciclo fuera de un ciclo, ni en el código ejecutado ni en las definiciones.

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