我正在开发一个小宏,在其中我创建了一个\newcommand
带有两个参数的宏。第二个参数只是一个名称。我想\newcommand
多次使用它,最后我想要一个使用的所有第二个参数的完整列表。当我使用它一两次时,它会起作用,但当我使用它三次时会失败并报告超出 tex 容量。我能做些什么吗?这里有一个小例子,可以说明这个问题。
\documentclass{article}
\usepackage{tikz}
\usepackage{ifthen}
\newcommand{\List}{}
\newcommand{\names}[2]{%
\foreach \i in {#1} {%
\i~belongs in #2.\par
}
\let\OldList\List
\ifthenelse{\equal{\OldList}{}}{\renewcommand{\List}{#2}}{\renewcommand{\List}{\OldList ,#2}}
}
\begin{document}
\names{Paul,Peter,Hans}{group1}
\names{Erik,Robert}{group2}
%\names{Alex,Robin,Klaus}{group3} %uncomment this line to produce error
The following groups participate: \List
\end{document}
答案1
我建议使用来自 的明确定义/测试过的宏etoolbox
。
可以\listgadd{\List}{}
定义一个列表。
\forcsvlist{\listgadd{\List}}{#1}
例如,可以用 添加逗号分隔的值列表。
使用宏\shownames[2]
可以循环遍历列表,当前列表条目包含在最后的宏的参数通过
\forlistloop{\shownames{#1}}{\List}
\documentclass{article}
\usepackage{etoolbox}%
\listgadd{\List}{} % Dummy list
\listgadd{\GroupList}{} % Dummy list
\newcommand{\shownames}[2]{%
#2 belongs in #1\par
}%
\newcommand{\showallnames}[1]{%
#1\par
}%
\newcommand{\List}{}
\newcommand{\names}[2]{%
\forcsvlist{\listgadd{\List}}{#1}%
\forcsvlist{\listgadd{\GroupList}}{#2}%
\forlistloop{\shownames{#2}}{\List}%
}%
\begin{document}
\names{Paul,Peter,Hans}{group1}
\names{Erik,Robert}{group2}
\names{Alex,Robin,Klaus}{group3}
The following groups participate:
\forlistloop{\showallnames}{\GroupList}%
\end{document}
答案2
让我们看看会发生什么。使用第一个\names{Paul,Peter,Hans}{group1}
命令,您重新定义\List
为包含group1
。使用第二个命令\names{Erik,Robert}{group2}
,\List
不再为空,因此您将其定义为
\OldList,group2
其中\OldList
扩展为group1
。
现在问题出现了:当你执行第三个命令时\let\OldList\List
,所以\OldList
会扩展为\OldList,group2
并且,当
\ifthenelse{\equal{\OldList}{}
执行后,LaTeX 会“一直”扩展:因此它会扩展 ,\OldList,group2
扩展为\OldList,group2,group2
,扩展为 ... 答对了!无限循环。您基本上不是想用另一瓶酒来装满一瓶,而是用另一瓶酒本身!
一个更好的方法是改为扩展\List
:不\let\OldList\List
,而只是
\ifthenelse{\equal{\List}{}}
{\renewcommand{\List}{#2}}
{\expandafter\renewcommand\expandafter\List\expandafter{\List,#2}}
答案3
\OldList
如果您将其定义为内容\List
而不是尝试使其成为相同的宏,它就会起作用。(注意:其他人将能够更好地解释这一点!)
\documentclass{article}
\usepackage{ifthen,pgffor}
\newcommand{\List}{}
\newcommand{\names}[2]{%
\foreach \i in {#1} {%
\i~belongs in #2.\par
}
\ifthenelse{\equal{\List}{}}{\renewcommand{\List}{#2}}{\expandafter\renewcommand\expandafter\List\expandafter{\List, #2}}% safer (see comments) though I don't actually understand why
}
\begin{document}
\names{Paul,P\'eter,Hans}{group1}
\names{Erik,Robert}{group2}
\names{Alex,Robin,Klaus}{group3}
The following groups participate: \List
\end{document}
我删除它tikz
只是因为它与你的 MWE 基本无关,而且是一个不需要加载的大包!但正如 cgnieder 指出的那样,你确实需要它,pgffor
所以我用它代替了它。
答案4
这是expl3
您的代码版本。\List
宏会从组列表中删除所有重复项,这样您就可以执行\names{...}{groupX}
两次,而无需groupX
在输出中出现两次。
\documentclass{article}
\pagestyle{empty}
\usepackage{xparse}
\ExplSyntaxOn
\seq_new:N \l_gilean_groups_seq
\NewDocumentCommand \names { m m }
{
\gilean_names:nn { #1 } { #2 }
}
\NewDocumentCommand \List { }
{
\par
\seq_remove_duplicates:N \l_gilean_groups_seq
\seq_use:Nn \l_gilean_groups_seq { \par }
}
\cs_new_protected:Npn \gilean_names:nn #1#2
{
\clist_map_inline:nn { #1 }
{
##1 ~ belongs ~ in ~ #2 \par
}
\seq_put_right:Nn \l_gilean_groups_seq { #2 }
}
\ExplSyntaxOff
\begin{document}
\names{Paul,Peter,Hans}{group1}
\names{Erik,Robert}{group2}
\names{Alex,Robin,Klaus}{group3}
\names{Max,Donald,Robert}{group1}
The following groups participate: \List
\end{document}
列出各个群组
我们现在有了\listgroups[<separator>]
列出所有组并将分隔符作为可选参数的。此外,还有\listnames[<separator>]{<group>}
列出所有成员的<group>
。
\documentclass{article}
\pagestyle{empty}
\usepackage{xparse}
\ExplSyntaxOn
\seq_new:N \l_gilean_names_seq
\seq_new:N \l_gilean_groups_seq
\NewDocumentCommand \names { m m }
{
\gilean_names:nn { #1 } { #2 }
}
\NewDocumentCommand \listnames { O{\par} m }
{
\int_zero:N \l_tmpa_int
\seq_clear:N \l_tmpa_seq
\seq_map_inline:Nn \l_gilean_names_seq
{
\int_incr:N \l_tmpa_int
\str_if_eq_x:nnT { #2 } { \seq_item:Nn \l_gilean_groups_seq { \l_tmpa_int } }
{ \seq_put_right:Nn \l_tmpa_seq { ##1 } }
}
\seq_use:Nn \l_tmpa_seq { #1 }
}
\NewDocumentCommand \listgroups { O{\par} }
{
\group_begin:
\seq_remove_duplicates:N \l_gilean_groups_seq
\seq_use:Nn \l_gilean_groups_seq { #1 }
\group_end:
}
\cs_new_protected:Npn \gilean_names:nn #1#2
{
\clist_map_inline:nn { #1 }
{
\seq_put_right:Nn \l_gilean_names_seq { ##1 }
\seq_put_right:Nn \l_gilean_groups_seq { #2 }
}
}
\ExplSyntaxOff
\begin{document}
\names{Paul,Peter,Hans}{group1}
\names{Erik,Robert}{group2}
\names{Alex,Robin,Klaus}{group3}
\names{Max,Donald,Robert}{group1}
The following groups exist:\par
\listgroups[, ]
These people are in group1:\par
\listnames[, ]{group1}
These people are in group2:\par
\listnames[, ]{group2}
\end{document}