另一个扩展问题是我无法猜测\edef
/的正确组合\expandafters
来开始工作。
背景:
下面我定义了\AddToCommaSeparateList
哪些用途\g@addto@macro
按照如何保存字符串的运行列表,然后逐个处理它们。当在 之外使用时,它可以按我的意愿工作\foreach
,并能正确处理包含逗号的列表成员:
问题:
但是当我尝试从 内部向此列表添加内容时\foreach
,我遇到了扩展问题。答案似乎很明显,我只需要扩展添加到列表中的内容。嗯,这应该不太难,至少我是这么认为的。所以我定义了\ExpandedAddToCommaSeparateList
,其中而不是
\g@addto@macro#1{{#2}}% used in `\AddToCommaSeparateList`
我只是使用:
\edef\ExpandedMember{#2}%
\g@addto@macro#1{{\ExpandedMember}}%
好吧,就像扩展问题通常出现的情况一样(至少对我来说),事情并没有像预期的那样顺利进行。在下面的 MWE 中,我不得不求助于 来\xdef
编译,但我只需要\edef
。
问题:
- 我需要向宏添加什么神奇的
\edef
and/or组合才能在 a 外部和内部使用它?\expandafter
\ExpandedAddToCommaSeparateList
\foreach
笔记:
- 我认为这段代码似乎很长,但我认为这是由于注释,并且第一阶段只是为了测试代码在基本使用中是否按预期工作。
- 问题输出以红色突出显示,它应该与蓝色输出相对应。
- 我真的只想要一个可以在两种情况下(内部和外部)工作的宏版本
\foreach
。我真的不知道我会在哪里不是想要扩展(至少在我的用例中)。因此,我尝试在添加到列表的宏中处理扩展问题,而不是在 中处理\foreach
。
代码:
\documentclass{article}
\usepackage{xcolor}
\usepackage{xstring}
\usepackage{pgffor}
\makeatletter
\newcommand*{\ResetCommaSeparatedList}[1]{\gdef#1{}}%
\newcommand*{\IfEmptyCommaSeparatedList}[3]{%
% #1 = listname
% #2 = code if list is empty
% #3 = code if list is non-empty
\IfStrEq{#1}{}{#2}{#3}%
}
\newcommand*{\AddToCommaSeparateList}[2]{%
% #1 = listname
% #2 = content to add to CSV list
%
% Don't add a leading comma in list
\IfEmptyCommaSeparatedList{#1}{}{\g@addto@macro#1{,}}%
%
% Extra brace group in case #2 contains a comma
\g@addto@macro#1{{#2}}%
}%
\newcommand*{\ExpandedAddToCommaSeparateList}[2]{% with expansion?
% #1 = listname
% #2 = content to add to CSV list
%
% Don't add a leading comma in list
\IfEmptyCommaSeparatedList{#1}{}{\g@addto@macro#1{,}}%
%
% Only need a \edef here, but usign \xdef to get this to compile
\xdef\ExpandedMember{#2}%
% Extra brace group in case #2 contains a comma
\g@addto@macro#1{{\ExpandedMember}}%
}%
\newcommand*{\DumpList}[1][black]{%
\par
MyList = \par
\foreach \x in \MyList {\hspace*{1cm}\textcolor{#1}{\x}\par}
}%
\makeatother
\newcommand*{\MyList}{}%
\begin{document}
\noindent\textbf{Show that this works outside of foreach}\par
\ResetCommaSeparatedList{\MyList}
\AddToCommaSeparateList{\MyList}{1}
\AddToCommaSeparateList{\MyList}{2}
\AddToCommaSeparateList{\MyList}{3}
\AddToCommaSeparateList{\MyList}{4, 5, 6}
\DumpList[blue]
% Repeat just to ensure that that \ResetCommaSeparatedList works
\ResetCommaSeparatedList{\MyList}
\AddToCommaSeparateList{\MyList}{x}
\AddToCommaSeparateList{\MyList}{y}
\AddToCommaSeparateList{\MyList}{z}
\AddToCommaSeparateList{\MyList}{a, b, c}
\DumpList
\bigskip\hrule
\bigskip\noindent
But, when the content is added via a \verb|\foreach| there are
expansion issues.
I want this to yield \textcolor{blue}{1, 2, 3}:
% --------------------------------- Problems start here
\ResetCommaSeparatedList{\MyList}
\foreach \x in {1,...,3} {%
% A check is made here so that only certain ones get added
% to the CSV list (omitted here as it is not relevant to issue)
\ExpandedAddToCommaSeparateList{\MyList}{\x}%
}
\DumpList[red]
\bigskip\noindent
Also, would prefer just one version of this macro, so this should
produce results identical to the first output in blue above:
\ResetCommaSeparatedList{\MyList}
\ExpandedAddToCommaSeparateList{\MyList}{1}
\ExpandedAddToCommaSeparateList{\MyList}{2}
\ExpandedAddToCommaSeparateList{\MyList}{3}
\ExpandedAddToCommaSeparateList{\MyList}{4, 5, 6}
\DumpList[red]
\end{document}
答案1
\ExpandedMember
没有和 没有 的替代版本\expandafter
:
\newcommand*{\ExpandedAddToCommaSeparateList}[2]{% with expansion?
% #1 = listname
% #2 = content to add to CSV list
%
% Don't add a leading comma in list
\IfEmptyCommaSeparatedList{#1}{}{\g@addto@macro#1{,}}%
% Extra brace group in case #2 contains a comma
\begingroup\edef\x{\endgroup
\noexpand\g@addto@macro\noexpand#1{{#2}}}\x
}
分组\edef\x
只是为了确保\x
在其自身扩展后不可用。它在一个组中定义,并且其扩展关闭了该组,因此定义就消失了。
LaTeX3 实现。有一个很大的变化:不再使用控制序列,而是为列表指定名称(为了统一,我还更改了几个宏名称)
\documentclass{article}
\usepackage{xcolor}
\usepackage{xparse}
\usepackage{pgffor}
\ExplSyntaxOn
\NewDocumentCommand{\ResetCommaSeparatedList} { m }
{
\clist_gclear_new:c { g_grill_list_ #1 _clist }
}
\NewDocumentCommand{\AddToCommaSeparatedList} { m m }
{
% #1 = listname
% #2 = content to add to CSV list
%
\clist_gput_right:cn { g_grill_list_ #1 _clist } { {#2} }
}
\NewDocumentCommand{\ExpandedAddToCommaSeparatedList} { m m }
{
% #1 = listname
% #2 = content to add to CSV list
\clist_gput_right:cx { g_grill_list_ #1 _clist } { {#2} }
}
\NewDocumentCommand{\DumpList} { O{black} m }
{
\par
#2 = \par
\clist_map_inline:cn { g_grill_list_ #2 _clist }
{
\hspace*{1cm}\textcolor{#1}{##1}\par
}
}
\ExplSyntaxOff
\newcommand{\Gx}{1}
\newcommand{\Gy}{2}
\newcommand{\Gz}{3}
\begin{document}
\noindent\textbf{Show that this works}\par
\ResetCommaSeparatedList{MyList}
\AddToCommaSeparatedList{MyList}{1}
\AddToCommaSeparatedList{MyList}{2}
\AddToCommaSeparatedList{MyList}{3}
\AddToCommaSeparatedList{MyList}{4, 5, 6}
\DumpList[blue]{MyList}
% Repeat just to ensure that that \ResetCommaSeparatedList works
\ResetCommaSeparatedList{MyList}
\AddToCommaSeparatedList{MyList}{x}
\AddToCommaSeparatedList{MyList}{y}
\AddToCommaSeparatedList{MyList}{z}
\AddToCommaSeparatedList{MyList}{a, b, c}
\DumpList{MyList}
\ResetCommaSeparatedList{MyList}
\foreach \x in {1,...,3} {%
\ExpandedAddToCommaSeparatedList{MyList}{\x}%
}
\DumpList[red]{MyList}
\bigskip
\ResetCommaSeparatedList{MyList}
\ExpandedAddToCommaSeparatedList{MyList}{\Gx}
\ExpandedAddToCommaSeparatedList{MyList}{\Gy}
\ExpandedAddToCommaSeparatedList{MyList}{\Gz}
\ExpandedAddToCommaSeparatedList{MyList}{4, 5, 6}
\DumpList[red]{MyList}
\end{document}
实际上,序列更适合此应用程序(例如,不需要额外的括号,并且处理速度可能更快)。
\documentclass{article}
\usepackage{xcolor}
\usepackage{xparse}
\usepackage{pgffor}
\ExplSyntaxOn
\NewDocumentCommand{\ResetList} { m }
{
\seq_gclear_new:c { g_grill_list_ #1 _seq }
}
\NewDocumentCommand{\AddToList} { m m }
{
% #1 = listname
% #2 = content to add to list
%
\seq_gput_right:cn { g_grill_list_ #1 _seq } { #2 }
}
\NewDocumentCommand{\ExpandedAddToList} { m m }
{
% #1 = listname
% #2 = content to add to list
\seq_gput_right:cx { g_grill_list_ #1 _seq } { #2 }
}
\NewDocumentCommand{\DumpList} { O{black} m }
{
\par
#2 = \par
\seq_map_inline:cn { g_grill_list_ #2 _seq }
{
\hspace*{1cm}\textcolor{#1}{##1}\par
}
}
\ExplSyntaxOff
\newcommand{\Gx}{1}
\newcommand{\Gy}{2}
\newcommand{\Gz}{3}
\begin{document}
\noindent\textbf{Show that this works}\par
\ResetList{MyList}
\AddToList{MyList}{1}
\AddToList{MyList}{2}
\AddToList{MyList}{3}
\AddToList{MyList}{4, 5, 6}
\DumpList[blue]{MyList}
% Repeat just to ensure that that \ResetList works
\ResetList{MyList}
\AddToList{MyList}{x}
\AddToList{MyList}{y}
\AddToList{MyList}{z}
\AddToList{MyList}{a, b, c}
\DumpList{MyList}
\ResetList{MyList}
\foreach \x in {1,...,3} {%
\ExpandedAddToList{MyList}{\x}%
}
\DumpList[red]{MyList}
\bigskip
\ResetList{MyList}
\ExpandedAddToList{MyList}{\Gx}
\ExpandedAddToList{MyList}{\Gy}
\ExpandedAddToList{MyList}{\Gz}
\ExpandedAddToList{MyList}{4, 5, 6}
\DumpList[red]{MyList}
\end{document}
您可能愿意添加
\NewDocumentCommand{\ExpandedOnceAddToList} { m m }
{
% #1 = listname
% #2 = content to add to list
\seq_gput_right:co { g_grill_list_ #1 _seq } { #2 }
}
与之一起使用\foreach
,以便在
\ResetList{MyList}
\foreach \x in {\Gx,\Gy,\Gz} {%
\ExpandedOnceAddToList{MyList}{\x}%
}
辅助宏\x
只会扩展一次,并且列表将包含\Gx
,\Gy
而\Gz
不是它们的扩展。
答案2
您想要添加的扩展\ExpandedMember
,而不是\ExpandedMember
其本身:
\documentclass{article}
\usepackage{xcolor}
\usepackage{xstring}
\usepackage{pgffor}
\makeatletter
\newcommand*{\ResetCommaSeparatedList}[1]{\gdef#1{}}%
\newcommand*{\IfEmptyCommaSeparatedList}[3]{%
% #1 = listname
% #2 = code if list is empty
% #3 = code if list is non-empty
\IfStrEq{#1}{}{#2}{#3}%
}
\newcommand*{\AddToCommaSeparateList}[2]{%
% #1 = listname
% #2 = content to add to CSV list
%
% Don't add a leading comma in list
\IfEmptyCommaSeparatedList{#1}{}{\g@addto@macro#1{,}}%
%
% Extra brace group in case #2 contains a comma
\g@addto@macro#1{{#2}}%
}%
\newcommand*{\ExpandedAddToCommaSeparateList}[2]{% with expansion?
% #1 = listname
% #2 = content to add to CSV list
%
% Don't add a leading comma in list
\IfEmptyCommaSeparatedList{#1}{}{\g@addto@macro#1{,}}%
%
% Only need a \edef here, but usign \xdef to get this to compile
\edef\ExpandedMember{#2}%
% Extra brace group in case #2 contains a comma
\expandafter\g@addto@macro\expandafter#1\expandafter{\expandafter{\ExpandedMember}}%
}%
\newcommand*{\DumpList}[1][black]{%
\par
MyList = \par
\foreach \x in \MyList {\hspace*{1cm}\textcolor{#1}{\x}\par}
}%
\makeatother
\newcommand*{\MyList}{}%
\begin{document}
\noindent\textbf{Show that this works outside of foreach}\par
\ResetCommaSeparatedList{\MyList}
\AddToCommaSeparateList{\MyList}{1}
\AddToCommaSeparateList{\MyList}{2}
\AddToCommaSeparateList{\MyList}{3}
\AddToCommaSeparateList{\MyList}{4, 5, 6}
\DumpList[blue]
% Repeat just to ensure that that \ResetCommaSeparatedList works
\ResetCommaSeparatedList{\MyList}
\AddToCommaSeparateList{\MyList}{x}
\AddToCommaSeparateList{\MyList}{y}
\AddToCommaSeparateList{\MyList}{z}
\AddToCommaSeparateList{\MyList}{a, b, c}
\DumpList
\bigskip\hrule
\bigskip\noindent
But, when the content is added via a \verb|\foreach| there are
expansion issues.
I want this to yield \textcolor{blue}{1, 2, 3}:
% --------------------------------- Problems start here
\ResetCommaSeparatedList{\MyList}
\foreach \x in {1,...,3} {%
% A check is made here so that only certain ones get added
% to the CSV list (omitted here as it is not relevant to issue)
\ExpandedAddToCommaSeparateList{\MyList}{\x}%
}
\DumpList[red]
\bigskip\noindent
Also, would prefer just one version of this macro, so this should
produce results identical to the first output in blue above:
\ResetCommaSeparatedList{\MyList}
\ExpandedAddToCommaSeparateList{\MyList}{1}
\ExpandedAddToCommaSeparateList{\MyList}{2}
\ExpandedAddToCommaSeparateList{\MyList}{3}
\ExpandedAddToCommaSeparateList{\MyList}{4, 5, 6}
\DumpList[red]
\end{document}
答案3
由于您的评论表明您的列表可能包含宏元素,因此如果是,\AddToCommaList
则将扩展累积。现在,将外括号添加到(并扩展)累积元素。\mywrapper
\@firstofone
\mywrapper
\documentclass{article}
\usepackage{xcolor}
\usepackage{loops}[2012/10/16]
\newcommand*\ResetCommaList[1]{\gdef#1{}}
% You can change \mywrapper to format added items:
\newcommand*\mywrapper[1]{{#1}}
\newcommand*\MyList{}
% Expand new item while adding:
\newcommand*\AddToCommaList[2]{\edef#1{\skvaddlist,#1\mywrapper{#2}}}
\newcommand*\DumpList[1][black]{%
\par MyList = \par
% PGF's \foreach will normally assume \MyList to contain the elements of
% the list. Since a list may be made up of \macro items, \foreachfox
% doesn't make that assumption. So you would need to tell it to expand
% the list. This can be done using the keys 'list is a macro',
% 'expand list once', 'expand list twice', etc.
\foreachfox [list is a macro] \MyList {%
\hspace*{1cm}\textcolor{#1}{##1}\endgraf
}%
}
\def\MyListOfValues{Rancheria, Paiute, Pascua, Paskenta}
\begin{document}
\noindent\textbf{Show that this works outside of foreach}\par
\ResetCommaList\MyList
\AddToCommaList\MyList\MyListOfValues
\AddToCommaList\MyList{1}
\AddToCommaList\MyList{2}
\AddToCommaList\MyList{3}
\AddToCommaList\MyList{4, 5, 6}
\DumpList[blue]
% Repeat printing:
\ResetCommaList\MyList
\AddToCommaList\MyList{x}
\AddToCommaList\MyList{y}
\AddToCommaList\MyList{z}
\AddToCommaList\MyList{a, b, c}
\DumpList
\bigskip\hrule\bigskip\noindent
\ResetCommaList\MyList
\def\ignoredlist{3,B}
\foreachfox {1,...,4,A,...,D} {%
% Decide if new item should be added:
\skvxiffound{,#1,}\in{,\ignoredlist,}\then\else
\AddToCommaList\MyList{#1}%
\fi
}
\DumpList[red]
\bigskip\noindent
\ResetCommaList\MyList
\AddToCommaList\MyList{1}
\AddToCommaList\MyList{2}
\AddToCommaList\MyList{3}
\AddToCommaList\MyList{4, 5, 6}
\DumpList[red]
\end{document}
另外,您可能考虑合并列表,而不是添加。在这种情况下,您可以调用方法
\skvmergelist{<listcmd>}{<newitems>}
或者
\skvfiltermergelist{<listcmd>}{<newitems>}{<filter>}