我希望能够让宏访问列表中的特定元素。以下代码可以工作,但需要我有两个单独的宏:一个接受列表,另一个接受包含列表的宏的名称。
\documentclass{article}
\usepackage{tikz}
\usetikzlibrary{calc}
\usepackage{xstring}
\begin{document}
\newcommand*{\GetListMemberA}[2]{%
\foreach \a [count=\i] in {#1} {%
\IfEq{\i}{#2}{\a\breakforeach}{}%
}%
\par%
}%
% This is same as above except does not have {} around #1
\newcommand*{\GetListMemberB}[2]{%
\foreach \a [count=\i] in #1 {%
\IfEq{\i}{#2}{\a\breakforeach}{}%
}%
\par%
}%
\newcommand*{\MyList}{a1,b2,c3,d4,e5}%
% These work if the foreach uses {#1} (ie, with the curly braces)
This should print "b": \GetListMemberA{a,b,c,d,e}{2}%
This should be blank: \GetListMemberA{a,b,c,d,e}{6}%
% These work if the foreach uses #1 (ie, without the curly braces}
This should be "a1": \GetListMemberB{\MyList}{1}%
This should be "c3": \GetListMemberB{\MyList}{3}%
This should be blank: \GetListMemberB{\MyList}{6}%
\end{document}
这个问题TikZ \foreach 循环与宏定义列表建议在使用 foreach 时删除宏周围的括号,这就是我想出这个解决方案的方法。
那么,我该如何改变这一点,以便不需要两个宏?
我不认为这个问题与在 TikZ/PGFplots 中使用宏定义列表因为这是特定于用于标记轴上刻度标记的列表。
答案1
为了使宏既能与内联列表一起工作,也能与宏中的列表一起工作,您可以使用 来\edef
组合循环命令,在\noexpand
每个宏之前(参数除外)使用:
\documentclass{article}
\usepackage{tikz}
\usetikzlibrary{calc}
\usepackage{xstring}
\begin{document}
% This works both with inline lists and with macros containing lists
\newcommand*{\GetListMember}[2]{%
\edef\dotheloop{%
\noexpand\foreach \noexpand\a [count=\noexpand\i] in {#1} {%
\noexpand\IfEq{\noexpand\i}{#2}{\noexpand\a\noexpand\breakforeach}{}%
}}%
\dotheloop
\par%
}%
\newcommand*{\MyList}{a1,b2,c3,d4,e5}%
This should print "b": \GetListMember{a,b,c,d,e}{2}%
This should be blank: \GetListMember{a,b,c,d,e}{6}%
This should be "a1": \GetListMember{\MyList}{1}%
This should be "c3": \GetListMember{\MyList}{3}%
This should be blank: \GetListMember{\MyList}{6}%
\end{document}
答案2
您可以使用\StrBetween
,并且您的代码不需要 calc 或 pgf:
\documentclass{article}
\usepackage{xstring}
\begin{document}
% This works both with inline lists and with macros containing lists
\newcommand*\GetListMember[2]{\StrBetween[#2,\number\numexpr#2+1]{,#1,},,\par}%
\newcommand*\MyList{a1,b2,c3,d4,e5}
This should print "b": \GetListMember{a,b,c,d,e}{2}
This should be blank: \GetListMember{a,b,c,d,e}{6}
This should be "a1": \GetListMember{\MyList}{1}
This should be "c3": \GetListMember{\MyList}{3}
This should be blank: \GetListMember{\MyList}{6}
\end{document}
答案3
宏\mypkg_expand:Nw
会根据函数参数是否带括号来扩展该参数。\GetListMember
只是通过该宏定义,\GetListMember_aux:nn
并将列表(扩展一次)作为第一个参数,将索引作为第二个参数。现在\clist_item:nn
从 1 开始计数。负参数从右侧开始,但需要最新版本的包expl3
。
\documentclass{article}
\usepackage{expl3}
\ExplSyntaxOn
\cs_new_protected_nopar:Npn \mypkg_expand:Nw #1
{
\peek_catcode_ignore_spaces:NTF \c_group_begin_token
{ #1 } { \exp_args:NV #1 }
}
\cs_new_nopar:Npn \GetListMember
{ \mypkg_expand:Nw \GetListMember_aux:nn }
\cs_new_nopar:Npn \GetListMember_aux:nn #1 #2
{
\clist_item:nn {#1} {#2}
\par
}
\ExplSyntaxOff
\begin{document}
\newcommand*{\MyList}{a1,b2,c3,d4,e5}
This should print ``b'': \GetListMember{a,b,c,d,e}{2}%
This should be blank: \GetListMember{a,b,c,d,e}{6}%
This should be ``a1'': \GetListMember\MyList{1}%
This should be ``c3'': \GetListMember\MyList{3}%
This should be blank: \GetListMember\MyList{6}%
\smallskip
This should print ``b'': \GetListMember{a,b,c,d,e}{-4}%
This should be blank: \GetListMember{a,b,c,d,e}{-6}%
This should be ``a1'': \GetListMember\MyList{-5}%
This should be ``c3'': \GetListMember\MyList{-3}%
This should be blank: \GetListMember\MyList{-6}%
\end{document}
答案4
当您尝试使用一个元素列表(恰好是控制序列)时,这种方法无法应对这种情况。根据后面是否有括号来判断是否不同是可能的,但我认为这不太可靠。
\documentclass{article}
\usepackage{xparse}
\ExplSyntaxOn
\NewExpandableDocumentCommand{\GetListMember}{mm}
{
\bool_lazy_and:nnTF { \tl_if_single_p:n { #1 } } { \token_if_cs_p:N #1 }
{% it's a control sequence
\clist_item:Nn #1 { #2 }
}
{
\clist_item:nn { #1 } { #2 }
}
}
\ExplSyntaxOff
\begin{document}
\newcommand*{\MyList}{a1,b2,c3,d4,e5}%
This should print ``b'': \GetListMember{a,b,c,d,e}{2}
This should be blank: \GetListMember{a,b,c,d,e}{6}
This should be ``a1'': \GetListMember{\MyList}{1}
This should be ``c3'': \GetListMember{\MyList}{3}
This should be blank: \GetListMember{\MyList}{6}
This should be ``a1'': \GetListMember\MyList{1}
This should be ``c3'': \GetListMember\MyList{3}
This should be blank: \GetListMember\MyList{6}
\end{document}
一般来说,*
当我们想要一个控制序列时,我更喜欢使用它(或者相反,根据您的喜好)。
\documentclass{article}
\usepackage{xparse}
\ExplSyntaxOn
\NewExpandableDocumentCommand{\GetListMember}{smm}
{
\IfBooleanTF{#1}
{% * means control sequence
\clist_item:Nn #2 { #3 }
}
{
\clist_item:nn { #2 } { #3 }
}
}
\ExplSyntaxOff
\begin{document}
\newcommand*{\MyList}{a1,b2,c3,d4,e5}%
This should print ``b'': \GetListMember{a,b,c,d,e}{2}
This should be blank: \GetListMember{a,b,c,d,e}{6}
This should be ``a1'': \GetListMember*{\MyList}{1}
This should be ``c3'': \GetListMember*{\MyList}{3}
This should be blank: \GetListMember*{\MyList}{6}
\end{document}