以下 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
打印一个控制字(即仅由字母组成的控制序列);第二个是因为我们在单词前面放置了一个明确的空格标记(
~
在语法中) 。expl3
in
类似地,\dontExpandMe
在输出中,后面恰好跟着一个空格,因为它是 打印的控制字\typeout
。
注 2:\unexpanded
必须使用才能按原样查看标记。这是因为\typeout
会展开其参数内的所有内容(如\message
,但\typeout
也是\protect
-aware,如中所述这个答案)。