获取列表的第 n 个元素(使用 etoolbox,或不使用)

获取列表的第 n 个元素(使用 etoolbox,或不使用)

我正在定义一个列表,我需要获取此列表的第 n 个元素。令我惊讶的是,它etoolbox没有提供获取它的方法。我发现获取第 n 个元素的方法是这样的:

\documentclass{minimal}

\usepackage{etoolbox}

\newcounter{mylistcounter}

\def\mylist{}
\forcsvlist{\listadd\mylist}{%
   first element,
   second element,
   third element,
   fourth element,
   fifth element
}%

\def\getnthelement#1{%
   \setcounter{mylistcounter}{1}%
   \renewcommand*\do[1]{%
      \ifnumequal{\value{mylistcounter}}{#1}{##1\listbreak}\relax
      \stepcounter{mylistcounter}%
   }%
   \dolistloop{\mylist}
}%

\begin{document}
\def\fourthelement{\getnthelement{4}} % <--- Fine!
% \edef\fourthelement{\getnthelement{4}} % <--- Problem!

\begin{itemize}
\item The third element is: ``\getnthelement{3}''.
\item The fourth element is: ``\fourthelement''.
\item The fourth element again: ``\fourthelement''.
\item The fifth element is: ``\getnthelement{5}''.
\end{itemize}
\end{document}

不幸的是,我需要多次使用相同的元素,并且我不想每次都循环遍历列表,因此我需要将其保存在某个地方。我以为这可以通过使用来实现\edef,但正如您在上面的 MWE 中看到的那样,使用\edef未注释的,我得到了错误:

! Undefined control sequence.
\GenericError  ...
                                                    #4  \errhelp \@err@     ...
l.27 \edef\fourthelement{\getnthelement{4}
                                              } % <--- Problem!
? h
The control sequence at the end of the top line
of your error message was never \def'ed. If you have
misspelled it (e.g., `\hobx'), type `I' and the correct
spelling (e.g., `I\hbox'). Otherwise just continue,
and I'll forget about whatever was undefined.

?

有什么办法可以解决这个问题吗?欢迎任何解决方案,即使不使用 的解决方案也是如此etoolbox。但请避免使用不稳定的软件包。此外,我需要一种通用方法,因为我需要多次使用列表中的所有元素,但总是在同一次使用。

答案1

如果您需要重复访问任意项目,那么命令名称的“数组” \mylist1, \mylist2\ ... 可能比列表更合适。

\documentclass{minimal}

\usepackage{etoolbox}

\newcounter{mylistcounter}

\def\saveitem#1{%
\stepcounter{mylistcounter}%
\expandafter\def\csname mylist\themylistcounter\endcsname{#1}}

\forcsvlist{\saveitem}{%
   first element,
   second element,
   third element,
   fourth element,
   fifth element
}%


\def\getnthelement#1{\csname mylist#1\endcsname}


\begin{document}


\begin{itemize}
\item The third element is: ``\getnthelement{3}''.
\item The fourth element is: ``\getnthelement{4}''.
\item The fourth element again: ``\getnthelement{4}''.
\item The fifth element is: ``\getnthelement{5}''.
\end{itemize}
\end{document}

答案2

这是一个可能的解决方案xparse

\documentclass{article}
\usepackage{xparse}
\ExplSyntaxOn
\NewDocumentCommand{\newgniourflist}{ m }
 {
  \seq_new:c { g_gniourf_#1_seq }
 }
\newgniourflist{gniourflist}
\NewDocumentCommand{\addtogniourflist}{ O{gniourflist} m }
 {
  \seq_gput_right:cn { g_gniourf_#1_seq } { #2 }
 }
\NewExpandableDocumentCommand{\getnthelement}{ O{gniourflist} m }
 {
  \seq_item:cn { g_gniourf_#1_seq } { #2 }
 }
\NewDocumentCommand{\storenthelement}{ O{gniourflist} m m }
 {
  \cs_set:Npx #3 { \seq_item:cn { g_gniourf_#1_seq } { #2 } }
 }
\NewDocumentCommand{\cleargniourflist}{ O{gniourflist} }
 {
  \seq_gclear:c { g_gniourf_#1_seq }
 }
\ExplSyntaxOff

\newcommand{\fourthelement}{\getnthelement{4}}

\begin{document}

\addtogniourflist{first element}
\addtogniourflist{second element}
\addtogniourflist{third element}
\addtogniourflist{fourth element}
\addtogniourflist{fifth element}

\begin{itemize}
\item The third element is: ``\getnthelement{3}''.
\item The fourth element is: ``\fourthelement''.
\item The fourth element again: ``\fourthelement''.
\item The fifth element is: ``\getnthelement{5}''.
\end{itemize}

\storenthelement{4}{\playaroundelement}

\texttt{\meaning\playaroundelement}

\end{document}

最后几行表明宏\playaroundelement定义为仅扩展为fourth element

使用这些宏,您可以管理多个列表;默认列表称为gniourflist;如果您想清除它,只需发出\cleargniourflist。但你可以说

\newgniourflist{anotherlist}

并像以前一样使用 newlist,只需添加一个可选参数:

\addtogniourflist[anotherlist]{something}
\getnthelement[anotherlist]{1}
\storenthelement[anotherlist]{1}{\someelement}
\cleargniourflist[anotherlist]

答案3

以下解决方案也很快,并且定义了一个宏来保存整个列表。David Carlisle 的方案定义的宏与列表项一样多。我不知道这个和 David Carlisle 的解决方案之间哪一个需要的资源更少。

\documentclass{minimal}
\usepackage{etoolbox}
\makeatletter
\newcount\listcount
\def\list@list{}
\def\do#1{%
  \advance\listcount\@ne
  \edef\list@list{%
    \unexpanded\expandafter{\list@list}%
    \the\listcount{\unexpanded{#1}}%
  }%
}
\forcsvlist\do{%
  first element,second element,third element,fourth element,fifth element
}
% \getelement{<number>}
\def\getelement#1{%
  \def\reserved@a##1#1##2##3\listmark{%
    \edef\reserved@a{\unexpanded{##2}}%
    \ifx\reserved@a\@nnil
      \@latexerr{No item number '#1'}\@ehd
    \else
      ##2%
    \fi
  }%
  \expandafter\reserved@a\list@list#1{\@nil}\listmark
}
\makeatother
\begin{document}
\begin{itemize}
\item The third element is: ``\getelement{3}''.
\item The fourth element is: ``\getelement{4}''.
\item The fourth element again: ``\getelement{4}''.
\item The fifth element is: ``\getelement{5}''.
% This gives error:
%\item The non-existent element is: ``\getelement{100}''.
\end{itemize}
\end{document}

如果你要美化你的列表,例如,

\forcsvlist\do{%
  first element  ,
  second element ,
  third element  ,
  fourth element ,
  fifth element
}

列表项前的空格可以通过\forcsvlist(via \@iden) 正确删除,但列表项后的空格则无法删除。在这种情况下,需要进行列表规范化。

即使不美化列表,后面的空格fifth element也会保留。看看你的输出。为了避免尾随空格,请在列表末尾添加注释符号:fifth element%

编辑

这是一个规范化列表并可扩展的解决方案。我仍然更喜欢这种迭代解决方案,而不是定义与列表项数量一样多的命令。

\documentclass{minimal}
\usepackage{catoptions}
\makeatletter
\newcount\gnilistcount
% \addlistitems{<listcmd>}{<items>}
\def\addlistitems#1#2{%
  \ifdefTF#1{}{\def#1{}}%
  \cptfor#2\dofor{%
    \advance\gnilistcount\@ne
    \edef#1{%
      \unexpanded\expandafter{#1}%
      \the\gnilistcount{\unexpanded{##1}}%
    }%
  }%
}
% \getelement{<number>}{<listcmd>}
\def\getelement#1#2{%
  \expandafter\gni@getelement#2\@nil\@nil\listmark{#1}%
}
\def\gni@getelement#1#2#3\listmark#4{%
  \ifstrcmpTF{#1}\@nil{%
    \@latexerr{No item number '#4'}\@ehd
  }{%
    \ifnumcmpTF#1=#4{%
      #2%
    }{%
      \gni@getelement#3\listmark{#4}%
    }%
  }%
}
 \makeatother

% Examples:
\addlistitems\gnilist{%
  first element  ,
  second element ,
  third element  ,
  fourth element ,
  fifth element
}
% Get third element in an \edef:
\edef\x{\getelement{3}\gnilist}
%\show\x

\begin{document}
\begin{itemize}
\item The third element is: ``\getelement{3}\gnilist''.
\item The fourth element is: ``\getelement{4}\gnilist''.
\item The fourth element again: ``\getelement{4}\gnilist''.
\item The fifth element is: ``\getelement{5}\gnilist''.
% This gives error:
%\item The non-existent element is: ``\getelement{100}\gnilist''.
\end{itemize}
\end{document}

这是一个更通用的方法\addlistitems,它在保存列表之前也会对其进行规范化。

\documentclass{minimal}
\usepackage{catoptions}
\makeatletter
% \addlistitems[<optional parser>]{<listcmd>}{<items>}
% \addlistitems*[<optional parser>]{<listcmd>}{<itemcmd>}
% The same item may be entered more than once, but with different
% serial numbers. If this isn't the desired spec, then the OP should
% say so.
\robust@def*\addlistitems{\cpt@teststopt\gni@addlistitems,}
\robust@def*\gni@addlistitems[#1]#2#3{%
  \begingroup
  \ifdefTF#2{}{\def#2{}}%
  \edef\tempb{\cptremovescape#2}%
  \ifcsndefTF{listcount@\tempb}{}{%
    \csn@xdef{listcount@\tempb}{0}%
  }%
  \def\csv@do##1{%
    \aftercsname\cptpushnumber{listcount@\tempb}%
    \edef#2{%
      \unexpanded\expandafter{#2}%
      \usecsn{listcount@\tempb}{\unexpanded{##1}}%
    }%
  }%
  \edef\tempa{\csv@@parse\ifcpt@st*\fi}%
  \tempa[#1]{#3}%
  \postgroupdef#2\endgroup
}
% \getelementofnumber{<number>}{<listcmd>}
% This can be used for a general list command <listcmd>.
\new@def*\getelementofnumber#1#2{%
  \expandafter\gni@getelement#2\@nil\@nil\listmark{#1}%
}
\new@def*\gni@getelement#1#2#3\listmark#4{%
  \ifstrcmpTF{#1}\@nil{%
    \@latexerr{No item number '#4'}\@ehd
  }{%
    \ifnumcmpTF#1=#4{%
      #2%
    }{%
      \gni@getelement#3\listmark{#4}%
    }%
  }%
}
\makeatother

% Example:
\addlistitems\gnilist{%
  element 1 ,
  element 2 ,
  element 3 ,
  element 4 ,
  element 5
}
% The list separator is changed here:
\addlistitems[;]\gnilist{%
  element 6  ;
  element 7  ;
  element 8  ;
  element 9  ;
  element 10 ;
  % If you don't want 'element 10' entered twice, say so:
  element 10 ;
}
%\show\gnilist

% Get element no. 3 in an \edef:
\edef\x{\getelementofnumber{3}\gnilist}
%\show\x

\def\getel#1{\getelementofnumber{#1}\gnilist}

\begin{document}
\begin{itemize}
\item The third element is: ``\getel{3}''.
\item The fourth element is: ``\getel{4}''.
\item The fourth element again: ``\getel{4}''.
\item The fifth element is: ``\getel{5}''.
\item The ninth element is: ``\getel{9}''.
% This gives error:
%\item The non-existent element is: ``\getel{100}''.
\end{itemize}
\end{document}

答案4

pgffor我知道,原始问题已经很老了,但也许有人正在寻找使用循环(来自包含在中的包tikz)和计数器的直接解决方案:

\documentclass{standalone}

\usepackage{pgffor}

\newcounter{itemcount}
\newcommand{\getitem}[2]{%
  \setcounter{itemcount}{0}%
  \foreach \i in #1 {%
    \ifnum\theitemcount=#2{\i}{}\fi%
    \stepcounter{itemcount}%
  }%
}

\def\mylist{a,b,c,d,e}

\begin{document}

  \getitem{\mylist}{0}
  \getitem{\mylist}{2}
  \getitem{\mylist}{4}
  \getitem{\mylist}{1}
  \getitem{\mylist}{3}

\end{document}

输出:

在此处输入图片描述

相关内容