访问列表特定成员的宏

访问列表特定成员的宏

我希望能够让宏访问列表中的特定元素。以下代码可以工作,但需要我有两个单独的宏:一个接受列表,另一个接受包含列表的宏的名称。

\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}

在此处输入图片描述

相关内容