\ pgfmathsetlengthmacro несовместим с циклом?

Sep 06 2020

Следующий код компилируется отлично:

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

Моя проблема в том, что я хочу раскомментировать \pgfmathsetlengthmacro\LeftWidth{width("\ImageLeft")}, но тогда код больше не компилируется. Как я могу это исправить?

Ответы

2 UlrichDiez Sep 06 2020 at 14:31

Проблема 1:

Вы можете использовать \pgfmathsetlengthmacro\LeftWidth{width("\ImageLeft")} вне в \foreach-loop. Но с \pgfmathsetlengthmacro\LeftWidth{width("\ImageLeft")}макро \ImageLeftиспользуется , который , в свою очередь , использует \UseImageLeftкоторый , в свою очередь , использует в \iто время как \iопределен , чтобы получить количество изображений только в пределах от \foreach-loop.

Проблема 2:

У вас \pgfmathsetlengthmacro\LeftWidth{width("\ImageLeft")}есть образец

\pgfmathsetlengthmacro\LeftWidth{⟨expression⟩}
с
⟨expression⟩ = width("\ImageLeft")
, а
width("\ImageLeft")
в свою очередь имеет образец с = .
width(⟨expression⟩)

⟨expression⟩"\ImageLeft"

Это означает, что \ImageLeftон оценивается как часть ⟨expression⟩дважды:

Первый раз, когда оценивается ⟨expression⟩формирующий аргумент \pgfmathsetlengthmacro\LeftWidth.

Второй раз, когда оценивается ⟨expression⟩формирующий аргумент width(...).

pgfmanual.pdf, раздел «95.2 Синтаксис математических выражений: операторы», в объяснении оператора -оператора:"x"

... Однако, поскольку каждое выражение перед анализом раскрывается с помощью \ edef, макросы (например, команды шрифтов, такие как \ tt или \ Huge) могут нуждаться в «защите» от этого раскрытия (например, \ noexpand \ Huge). В идеале вам все равно следует избегать таких макросов. Очевидно, что эти операторы следует использовать с большой осторожностью, поскольку дальнейшие вычисления маловероятны с полученным результатом.

Эта информация не относится только к оператору. Эта информация актуальна всякий раз, когда дело доходит до оценки . Это общее правило, которое применяется при оценке каждого . Поэтому я думаю, что передавать эту информацию только при объяснении специального оператора - не лучшая идея."x"⟨expression⟩⟨expression⟩

Как бы то ни было:

Собирая информацию вместе, вы обнаруживаете, что \ImageLeftона оценивается \edefдважды. Поскольку цепочка раскрытия \ImageLeft→ \UseImageLeft→ ...\includegraphics..., это означает, что имеют место две попытки вычисления \includegraphicsvia \edef.
Но в случае, если вы не используете один из самых последних TeX-дистрибутивов, \includegraphicsэто не работает в \edef-expansion-контекстах. Это связано с тем, что в старых TeX-дистрибутивах \includegraphicsне «защищен» - «защищен» относится к внутреннему механизму LaTeX для предотвращения расширения команд в ситуациях, когда это расширение не сработает - и «полагается на» выполнение временных назначений, например, с точки зрения \def, пока такие задания не выполняются, пока \edefидет оценка. \edefзапускает расширение расширяемых токенов, пока \defявляется нерасширяемым примитивом.

Поэтому, если вы не используете один из самых последних TeX-дистрибутивов, вам необходимо принять меры предосторожности для предотвращения расширения / оценки команды \ImageLeft(и, таким образом, предотвращения расширения / оценки \includegraphics) с каждым из этих \edefоценок. (В самых последних дистрибутивах TeX эти меры предосторожности не доставляют неудобств и не требуются.)

Вы можете предотвратить расширение / оценка предваряя \ImageLeftс \noexpand\noexpand\noexpand.

Первая \edef-Оценка, наносит на <code>\noexpand\noexpand\noexpand\ImageLeft</code>,
урожайность
<code>\noexpand\ImageLeft</code>.
Вторая \edef-Оценка будет применяться на то, что , в свою очередь , урожайности:
<code>\ImageLeft</code>.

Проблема 3:

egreg указал, что \iэто уже определено в LaTeX и что вы, вероятно, не хотите отменять это определение. Поэтому в приведенном ниже примере я использовал \NiceForEachElementвместо \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}

Если вы хотите иметь возможность использовать и \ImageLeftвне цикла, определите \ImageLeftetc для обработки аргумента вместо обработки \i/ \NiceForEachElement.
Внутри цикла вы можете передать \i/ в \NiceForEachElementкачестве аргумента.
Вне цикла вы можете напрямую передать номер изображения в качестве аргумента.

\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

Не используйте переменные цикла вне цикла, ни в исполняемом коде, ни в определениях.

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