使用 foreach 进行 csuse

使用 foreach 进行 csuse

很难确定使用 \csusefrom 的 正确语法包裹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}

归功于这个答案

相关内容