我正在定义一个列表,我需要获取此列表的第 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}
输出: