嵌套循环处理图片组

嵌套循环处理图片组

以下 MWE 处理图片组,将它们放在幻灯片上,并在图片下方添加标题。幻灯片是使用该pdfscreen包制作的,图片和标题的放置是通过创建tikz节点来完成的。

图片及其标题的列表是这样创建的,例如:\newcommand*{SetA}{pic1/caption 1,pic2/caption 2}。图片集位于\newcommand*{\MySets}{SetA,SetB}

我使用foreach循环来处理每组图片。我的问题是:如何将循环包裹foreach在内循环中来处理所有图片集?

\documentclass[a4paper,11pt]{article}
\usepackage{etoolbox}
\usepackage[dvipsnames,svgnames,x11names]{xcolor}
\usepackage{tikz}
\usepackage[T1]{fontenc}
\usepackage[nopanel,screen]{pdfscreen}
\margins{1cm}{1cm}{1cm}{1cm}
\screensize{15.875cm}{20.32cm}

\newcommand*{\SetA}{example-image-a/caption A1,example-image-a/caption A2}
\newcommand*{\SetB}{example-image-b/caption B1,example-image-b/caption B2}
\newcommand*{\MySets}{\SetA,\SetB}

\begin{document}
%\foreach \Set in {\MySets}{
\foreach \myphoto/\mycaption in \SetA {% How to change \SetA to \Set to process all the pictures in each set
\centering
\begin{tikzpicture}
\node (a) at (0,0) {\includegraphics[width=0.9\linewidth,height=0.9\textheight]{\myphoto}};
\node[below=3mm] (b) at (a.south) {\mycaption};
\end{tikzpicture}
\clearpage
}
%}

\end{document} 

答案1

对于这个特定的应用程序,外部\foreach使用列表列表,您可以在启动内部循环之前扩展包含内部列表的宏。

\documentclass[a4paper,11pt]{article}
\usepackage{etoolbox}
\usepackage[dvipsnames,svgnames,x11names]{xcolor}
\usepackage{tikz}
\usepackage[T1]{fontenc}
\usepackage[nopanel,screen]{pdfscreen}
\margins{1cm}{1cm}{1cm}{1cm}
\screensize{15.875cm}{20.32cm}

\newcommand*{\SetA}{example-image-a/caption A1,example-image-a/caption A2}
\newcommand*{\SetB}{example-image-b/caption B1,example-image-b/caption B2}
\newcommand*{\MySets}{\SetA,\SetB}

\newcommand{\eforeach}[2]{\expandafter\doeforeach\expandafter{#1}{#2}}
\newcommand{\doeforeach}[2]{\foreach #2 in #1}

\begin{document}

\centering
\foreach \Set in \MySets {
  \eforeach{\Set}{\myphoto/\mycaption} {%
    \begin{tikzpicture}
    \node (a) at (0,0) {\includegraphics[width=0.9\linewidth,height=0.9\textheight]{\myphoto}};
    \node[below=3mm] (b) at (a.south) {\mycaption};
    \end{tikzpicture}
    \clearpage
  }
}

\end{document} 

答案2

如果您对集合的命名一致,则可以循环遍历字符(在本例中为A和)并使用和B构建宏。MWE:\csname\endcsname

\documentclass[a4paper,11pt]{article}
\usepackage{etoolbox}
\usepackage[dvipsnames,svgnames,x11names]{xcolor}
\usepackage{tikz}
\usepackage[T1]{fontenc}
\usepackage[nopanel,screen]{pdfscreen}
\margins{1cm}{1cm}{1cm}{1cm}
\screensize{15.875cm}{20.32cm}

\newcommand*{\SetA}{example-image-a/caption A1,example-image-a/caption A2}
\newcommand*{\SetB}{example-image-b/caption B1,example-image-b/caption B2}
\newcommand*{\MySets}{\SetA,\SetB}

\begin{document}
  \foreach \id in {A,B}{
    \edef\Set{\csname Set\id\endcsname}
    \foreach \myphoto/\mycaption in \Set {
      \centering
      \begin{tikzpicture}
          \node (a) at (0,0) {\includegraphics[width=0.9\linewidth,height=0.9\textheight]{\myphoto}};
        \node[below=3mm] (b) at (a.south) {\mycaption};
      \end{tikzpicture}
      \clearpage
    }
  }
\end{document} 

答案3

这里有两件事:

\MySets首先,您需要省略外部的括号\foreach

其次,\foreach这样写,内循环相当于

\foreach \myphoto/\mycaption in {\SetA} {

当您尝试以您想要的方式循环时。对于您的情况,我们可以通过扩展第一个之前的所有内容来解决这个问题\foreach

\documentclass[a4paper,11pt]{article}
\usepackage{etoolbox}
\usepackage[dvipsnames,svgnames,x11names]{xcolor}
\usepackage{tikz}
\usepackage[T1]{fontenc}
\usepackage[nopanel,screen]{pdfscreen}
\margins{1cm}{1cm}{1cm}{1cm}
\screensize{15.875cm}{20.32cm}

\newcommand*{\SetA}{example-image-a/caption A1,example-image-a/caption A2}
\newcommand*{\SetB}{example-image-b/caption B1,example-image-b/caption B2}
\edef\MySets{{\SetA},{\SetB}}

\begin{document}

\foreach \Set in \MySets {
    \foreach \myphoto/\mycaption in \Set {
        \centering
        \begin{tikzpicture}
            \node (a) at (0,0) {\includegraphics[width=0.9\linewidth,height=0.9\textheight]{\myphoto}};
            \node [below=3mm] (b) at (a.south) {\mycaption};
        \end{tikzpicture}
        \clearpage
    }
}

\end{document}

如果您的用例不比这更复杂,您也可以首先避免嵌套的宏定义:

\newcommand*\MySets{%
    {   example-image-a/caption A1,
        example-image-a/caption A2},
    {   example-image-b/caption B1,
        example-image-b/caption B2}%
}

答案4

一半是为了好玩,这里有一个使用 LaTeX3的解决方案。它负责扩展您的\SetA和宏:\SetB\MySets

  • 足够早地使人\foreach快乐,并且;

  • 恰好一次,这样万一您的内部列表(\SetA\SetB)包含不应扩展的内容,或者不应过早扩展的内容,则该解决方案可以按原样使用(与使用\edef\MySets{{\SetA},{\SetB}}或类似内容的解决方案相反,后者完全扩展\SetA\SetB)。

\documentclass{article}
\usepackage[T1]{fontenc}
\usepackage{tikz}
\usepackage{xparse}
\usepackage[nopanel,screen]{pdfscreen}
\margins{1cm}{1cm}{1cm}{1cm}
\screensize{15.875cm}{20.32cm}

\ExplSyntaxOn

\clist_new:N \SetA
\clist_new:N \SetB
\clist_new:N \MySets

\clist_gset:Nn \SetA {example-image-a/caption~A1, example-image-a/caption~A2}
\clist_gset:Nn \SetB {example-image-b/caption~B1, example-image-b/caption~B2}
\clist_gset:Nn \MySets {\SetA, \SetB}

\cs_new_protected:Npn \ross_iterate_on_sets:NN #1#2
  {
    \clist_map_variable:NNn #2 #1
  }

\NewDocumentCommand \RossIterateOnSets { m m }
  {
    \ross_iterate_on_sets:NN #1 #2
  }

\cs_new_protected:Npn \ross_iterate_on_pics:nn #1#2
  {
     \foreach #1 ~in~ {#2}
  }

\cs_generate_variant:Nn \ross_iterate_on_pics:nn { nV }

\NewDocumentCommand \RossIterateOnPics { m m }
  {
    % First expansion of #2 (e.g. \Set -> \SetA or \SetB)
    \exp_args:Nno
    % Second expansion: use the *value* of \SetA or \SetB for the second
    % argument of \ross_iterate_on_pics:nn
    \ross_iterate_on_pics:nV {#1} {#2}
  }

\ExplSyntaxOff

\begin{document}

\RossIterateOnSets{\Set}{\MySets} {%
  \RossIterateOnPics{\myphoto/\mycaption}{\Set} {%
    \centering
    \begin{tikzpicture}
      \node (a) at (0,0) {%
        \includegraphics[width=0.9\linewidth,height=0.9\textheight]{\myphoto}};
      \node[below=3mm] (b) at (a.south) {\mycaption};
    \end{tikzpicture}
    \clearpage
  }%
}
\end{document}

如果逗号列表的某个元素包含逗号,则只需将整个元素括在括号中。

为了亲眼看到事物在被输入之前按照宣传的那样展开(即,和在到达 手中之前\foreach每个都只展开一次),请考虑这个稍微修改过的版本,我们将内循环所做的操作打印到终端和日志文件中:\SetA\SetB\MySets\foreach

\documentclass{article}
\usepackage{xparse}

\newcommand*{\dontExpandMe}{If this gets printed, it is GAME OVER.}

\ExplSyntaxOn

\clist_new:N \SetA
\clist_new:N \SetB
\clist_new:N \MySets

\clist_gset:Nn \SetA {example-image-a/caption~\dontExpandMe A1,
                      example-image-a/caption~A2}
\clist_gset:Nn \SetB {example-image-b/caption~B1,
                      example-image-b/caption~\dontExpandMe B2}
\clist_gset:Nn \MySets {\SetA, \SetB}

\cs_new_protected:Npn \ross_iterate_on_sets:NN #1#2
  {
    \clist_map_variable:NNn #2 #1
  }

\NewDocumentCommand \RossIterateOnSets { m m }
  {
    \ross_iterate_on_sets:NN #1 #2
  }

\cs_new_protected:Npn \ross_iterate_on_pics:nn #1#2
  {
     \typeout { \string\foreach \unexpanded {#1} ~in~ \unexpanded {{#2}} }
  }

\cs_generate_variant:Nn \ross_iterate_on_pics:nn { nV }

\NewDocumentCommand \RossIterateOnPics { m m }
  {
    % First expansion of #2 (e.g. \Set -> \SetA or \SetB)
    \exp_args:Nno
    % Second expansion: use the *value* of \SetA or \SetB for the second
    % argument of \ross_iterate_on_pics:nn
    \ross_iterate_on_pics:nV {#1} {#2}
  }

\ExplSyntaxOff

\begin{document}

\RossIterateOnSets{\Set}{\MySets} {%
  \RossIterateOnPics{\myphoto/\mycaption}{\Set}
}
\end{document}

\typeout以下是此示例的两个命令的输出(在终端上和日志文件中) :

\foreach\myphoto /\mycaption  in {example-image-a/caption \dontExpandMe A1,example-image-a/caption A2}
\foreach\myphoto /\mycaption  in {example-image-b/caption B1,example-image-b/caption \dontExpandMe B2}

如您所见,宏没有被展开。在完整示例中,这意味着在执行\dontExpandMe之前它不会展开。\foreach

注意:\mycaption此输出中后面有两个空格:

  • 第一个是\typeout打印一个控制字(即仅由字母组成的控制序列);

  • 第二个是因为我们在单词前面放置了一个明确的空格标记(~在语法中) 。expl3in

类似地,\dontExpandMe在输出中,后面恰好跟着一个空格,因为它是 打印的控制字\typeout

注 2:\unexpanded必须使用才能按原样查看标记。这是因为\typeout会展开其参数内的所有内容(如\message,但\typeout也是\protect-aware,如中所述这个答案)。

相关内容