很难确定使用
\csuse
from 的 正确语法包裹etoolbox
与\foreach
。
我有一个清单,每个我需要创建一个新列表。为了简化 MWE,我过去常常\IfStrEq
使用 来定义新列表\csgdef
。
第二个问题出在\foreach
我有一个额外的花括号:
{\csuse{ContentFor\x}}
这应该不是需要按照TikZ \foreach 循环与宏定义列表。
如果我省略{}
第二个,\foreach
我会得到:
\csuse 的参数有一个额外的 }
通过它{}
的编译我得到:
然而,期望的输出是:
代码:
\documentclass{article}
\usepackage{xstring}
\usepackage{etoolbox}
\usepackage{pgffor}
\newcommand*{\MyList}{ABC,XYZ}%
\begin{document}
%% For each element of \MyList I need to create a new list.
\foreach \x in \MyList {%
% In my actual use case the list that gets defined for each element of \MyList
% is based on timestamps of files, but as that is not relevant to this
% particular issue I left out that complexity, to make this test case easier to read.
\IfStrEq{\x}{ABC}{%
\csgdef{ContentFor\x}{FileA,FileB,FileC}%
}{%
\csgdef{ContentFor\x}{FileX,FileY,FileZ}%
}%
}%
% Now, only after I have ALL the lists created, I want to print EACH list
% for each element of \MyList.
\foreach \x in \MyList {%
\par\x=
\foreach \y in {\csuse{ContentFor\x}} {% <--- The {} around \csuse{} should NOT be necessary.
\par\hspace*{1.0cm}\y
}%
}%
%%% This is the expected output:
% \par ABC=
% \foreach \y in {FileA,FileB,FileC} {%
% \par\hspace*{1.0cm}\y
% }%
% \par XYZ=
% \foreach \y in {FileX,FileY,FileZ} {%
% \par\hspace*{1.0cm}\y
% }%
\end{document}
答案1
这是常见的扩展问题。如果\foreach
找到括号,它不会进行扩展,而是需要一个列表,在本例中只有一个元素,即\csuse{ContentFor\x}
。否则,它会扩展一次后面的标记和扩展应该是一个列表,放在括号中。但是,第一级扩展\csuse{ContentFor\x}
是
\ifcsname ContentFor\x\endcsname\csname\ContentFor\x\expandafter\endcsname\fi
这绝对不是一个清单。
您必须完全展开\csuse
。根据列表中预期的内容,您需要以下两种方法之一。
第一种方法
常用的全扩展方法
\foreach \x in \MyList {%
\par\x=
\begingroup\edef\x{\endgroup\noexpand\foreach\noexpand\y in {\csuse{ContentFor\x}}}\x {%
\par\hspace*{1.0cm}\y
}%
}
如果列表是完全可扩展的,即它只包含 ASCII 字符,则可行。如果列表可能包含“危险”项目,例如\textbf
.
第二种方法
如果列表包含“危险”项目,则必须限制其可扩展性:
\foreach \x in \MyList {%
% In my actual use case the list that gets defined for each element of \MyList
% is based on timestamps of files, but as that is not relevant to this
% particular issue I left out that complexity, to make this test case easier to read.
\IfStrEq{\x}{ABC}{%
\protected\csgdef{ContentFor\x}{FileA,FileB,FileC}%
}{%
\protected\csgdef{ContentFor\x}{FileX,FileY,FileZ}%
}%
}
\foreach \x in \MyList {%
\par\x=
\begingroup\edef\x{\endgroup\noexpand\foreach\noexpand\y in {\csuse{ContentFor\x}}}\x {%
\par\hspace*{1.0cm}\y
}%
}
请注意,\csuse
需要二扩展步骤以达到,比如说\ContentForABC
。
只是为了好玩,使用xparse
和实现expl3
:
\documentclass{article}
\usepackage{xparse}
\ExplSyntaxOn
\NewDocumentCommand{\createlist}{ m m }
{
\clist_new:c { g_grill_list_#1_clist }
\clist_gset:cn { g_grill_list_#1_clist } { #2 }
}
\NewDocumentCommand{\createcontents}{ m m }
{
\clist_new:c { g_grill_list_content_#1_clist }
\clist_set:cn { g_grill_list_content_#1_clist } { #2 }
}
\NewDocumentCommand{\processlist}{ m +m }
{
\clist_map_inline:cn { g_grill_list_#1_clist } { #2 }
}
\NewDocumentCommand{\processcontents}{ m +m }
{
\clist_map_inline:cn { g_grill_list_content_#1_clist } { #2 }
}
\ExplSyntaxOff
\createlist{MyList}{ABC,XYZ}
\createcontents{ABC}{FileA, FileB, FileC}
\createcontents{XYZ}{FileX, FileY, FileZ}
\begin{document}
\processlist{MyList}{%
\par#1=
\processcontents{#1}{\par\hspace*{1cm}##1}%
}
\end{document}
在和的第二个参数中\processlist
,\processcontents
您可以将当前项目(\x
和\y
在您的\foreach
语句中)引用为#1
;在内部,\processcontents
您必须将加倍#
。
答案2
这是使用 TikZ 的“现代”解决方案expand list
——以防将来有人遇到这个问题:
%! TEX program = lualatex
\documentclass{article}
\usepackage{xstring}
\usepackage{etoolbox}
\usepackage{pgffor}
\newcommand*{\MyList}{ABC,XYZ}%
\begin{document}
%% For each element of \MyList I need to create a new list.
\foreach \x in \MyList {%
% In my actual use case the list that gets defined for each element of \MyList
% is based on timestamps of files, but as that is not relevant to this
% particular issue I left out that complexity, to make this test case easier to read.
\IfStrEq{\x}{ABC}{%
\csgdef{ContentFor\x}{FileA,FileB,FileC}%
}{%
\csgdef{ContentFor\x}{FileX,FileY,FileZ}%
}%
}%
% Now, only after I have ALL the lists created, I want to print EACH list
% for each element of \MyList.
\foreach \x in \MyList {%
\par\x=
\foreach[expand list] \y in {\csuse{ContentFor\x}} {
\par\hspace*{1.0cm}\y
}%
}%
\end{document}
归功于这个答案。