我正在尝试编写一个宏,从逗号分隔的列表中选择一个项目,
\select{2}{A,B,C,D,E}
结果为B
。我正在使用xfor
包来处理列表,但etoolbox
如果它提供了更好的解决方案,我会尝试。现在的问题是:宏对于纯文本工作正常,但是如果项目是数字,我会得到一个未关闭的\ifnum
。
!不完整的 \ifnum;第 29 行之后的所有文本被忽略。 \fi
这是最小的例子
\documentclass{article}
\usepackage{xfor}
\usepackage{biblatex}
\makeatletter
\newcount\select@count
\newcount\select@i
\newcommand\select[2]{%
\select@i=1\select@count=0%
\@for\@@select@item:={#2}\do{\advance\select@count by1}%
\ifnum#1>\select@count??\else%
\@for\@@select@item:={#2}\do{%
\advance\select@i by1%
\ifnum#1<1??\@endfortrue\fi%
\ifnum\select@i=#1\@@select@item\@endfortrue\fi%
}%
\fi%
}
\makeatother
\begin{document}
\begin{itemize}
\item \verb+\select{0}{A,B,C,D,E}+ gives \select{0}{A,B,C,D,E}
\item \verb+\select{1}{A,B,C,D,E}+ gives \select{1}{A,B,C,D,E}
\item \verb+\select{2}{A,B,C,D,E}+ gives \select{2}{A,B,C,D,E}
\item \verb+\select{5}{A,B,C,D,E}+ gives \select{5}{A,B,C,D,E}
\item \verb+\select{10}{A,B,C,D,E}+ gives \select{10}{A,B,C,D,E}
% these do not work
% \item \verb+\select{1}{1,2,3,4,5}+ gives \select{1}{1,2,3,4,5}
% \item \verb+\select{2}{1,2,3,4,5}+ gives \select{2}{1,2,3,4,5}
\end{document}
有没有想过为什么它无法处理数字?我需要保护我的列表吗?
答案1
主要错误在于这一行
\ifnum\select@i=#1\@@select@item\@endfortrue\fi%
当\@@select@item
对数字的查找尚未完成,并且 TeX 不断扩展直到找到非数字的内容时;当 时\@@select@item
,1
很容易预测混乱。更好的风格是
\ifnum#1=\select@i\@@select@item\@endfortrue\fi
因为\select@i
是一个计数器,所以<number>
它所代表的是完整的。
但是你错了:在非数字输入的情况下,\@@select@item
会发现前物品。
这是一个工作版本;为了避免常数后面的扩展问题,最好使用\@ne
常数 1 和\z@
常数 0。
\documentclass{article}
\usepackage{xfor}
\makeatletter
\newcount\select@count
\newcount\select@i
\newcommand\select[2]{%
\select@i=\z@\select@count=\z@
\@for\@@select@item:={#2}\do{\advance\select@count by\@ne}%
\ifnum#1>\select@count??\else
\@for\@@select@item:={#2}\do{%
\advance\select@i by\@ne
\ifnum#1<1??\@endfortrue\fi
\ifnum#1=\select@i\@@select@item\@endfortrue\fi
}%
\fi
}
\makeatother
\begin{document}
\begin{itemize}
\item \verb+\select{0}{A,B,C,D,E}+ gives \select{0}{A,B,C,D,E}
\item \verb+\select{1}{A,B,C,D,E}+ gives \select{1}{A,B,C,D,E}
\item \verb+\select{2}{A,B,C,D,E}+ gives \select{2}{A,B,C,D,E}
\item \verb+\select{5}{A,B,C,D,E}+ gives \select{5}{A,B,C,D,E}
\item \verb+\select{10}{A,B,C,D,E}+ gives \select{10}{A,B,C,D,E}
\item \verb+\select{0}{1,2,3,4,5}+ gives \select{0}{1,2,3,4,5}
\item \verb+\select{1}{1,2,3,4,5}+ gives \select{1}{1,2,3,4,5}
\item \verb+\select{2}{1,2,3,4,5}+ gives \select{2}{1,2,3,4,5}
\item \verb+\select{5}{1,2,3,4,5}+ gives \select{5}{1,2,3,4,5}
\item \verb+\select{10}{1,2,3,4,5}+ gives \select{10}{1,2,3,4,5}
\end{itemize}
\end{document}
请注意,的初始设置\select@i
为 0。
只有一个计数器和一个循环的替代版本:
\makeatletter
\newcount\select@count
\newcommand\select[2]{%
\ifnum#1<\@ne??\else
\select@count=\z@
\@for\@@select@item:={#2,\@@nil}\do{%
\advance\select@count\@ne
\expandafter\ifx\@@select@item\@@nil
??\@endfortrue
\else
\ifnum#1=\select@count
\@@select@item\@endfortrue
\fi
\fi}%
\fi
}
\makeatother
不可避免的 LaTeX3 解决方案,它比基于它的解决\@for
方案有一个很大的优势:它是完全可扩展的。
\documentclass{article}
\usepackage{xparse}
\ExplSyntaxOn
\DeclareExpandableDocumentCommand \select { m m }
{
\egreg_select:nn { #1 } { #2 }
}
\cs_new:Npn \egreg_select:nn #1 #2
{
\bool_if:nTF
{
\int_compare_p:n { #1 < 1 }
||
\int_compare_p:n { #1 > \clist_count:n { #2 } }
}
{
??
}
{
\clist_item:nn { #2 } { #1 }
}
}
\ExplSyntaxOff
\begin{document}
\begin{itemize}
\item \verb+\select{0}{A,B,C,D,E}+ gives \select{0}{A,B,C,D,E}
\item \verb+\select{1}{A,B,C,D,E}+ gives \select{1}{A,B,C,D,E}
\item \verb+\select{2}{A,B,C,D,E}+ gives \select{2}{A,B,C,D,E}
\item \verb+\select{5}{A,B,C,D,E}+ gives \select{5}{A,B,C,D,E}
\item \verb+\select{10}{A,B,C,D,E}+ gives \select{10}{A,B,C,D,E}
%these do not work
\item \verb+\select{1}{1,2,3,4,5}+ gives \select{1}{1,2,3,4,5}
\item \verb+\select{2}{1,2,3,4,5}+ gives \select{2}{1,2,3,4,5}
\end{itemize}
% Let's show it's fully expandable!
\edef\x{\select{2}{1,2,3,4,5}}\x
\end{document}
如果您想为第二个参数提供宏,则需要在函数生效之前将其扩展。
\documentclass{article}
\usepackage{xparse}
\ExplSyntaxOn
\DeclareExpandableDocumentCommand \select { m m }
{
\egreg_select:on { #2 } { #1 }
}
\cs_new:Npn \egreg_select:nn #1 #2
{
\bool_if:nTF
{
\int_compare_p:n { #2 < 1 }
||
\int_compare_p:n { #2 > \clist_count:n { #1 } }
}
{
??
}
{
\clist_item:nn { #1 } { #2 }
}
}
\cs_generate_variant:Nn \egreg_select:nn { o }
\ExplSyntaxOff
\newcommand{\alist}{X,Y,Z}
\begin{document}
\begin{itemize}
\item \verb+\select{0}{A,B,C,D,E}+ gives \select{0}{A,B,C,D,E}
\item \verb+\select{1}{A,B,C,D,E}+ gives \select{1}{A,B,C,D,E}
\item \verb+\select{2}{A,B,C,D,E}+ gives \select{2}{A,B,C,D,E}
\item \verb+\select{5}{A,B,C,D,E}+ gives \select{5}{A,B,C,D,E}
\item \verb+\select{10}{A,B,C,D,E}+ gives \select{10}{A,B,C,D,E}
%these do not work
\item \verb+\select{1}{1,2,3,4,5}+ gives \select{1}{1,2,3,4,5}
\item \verb+\select{2}{1,2,3,4,5}+ gives \select{2}{1,2,3,4,5}
\end{itemize}
\verb+\select{2}{\alist}+ gives \select{2}{\alist}
% Let's show it's fully expandable!
\edef\x{\select{2}{1,2,3,4,5}}\x
\end{document}
请注意,为了提高效率,我颠倒了参数的顺序\egreg_select:nn
(第一个是列表,第二个是数字;但这不会改变用户级命令的语法。
答案2
这是一个etoolbox
执行:
\documentclass{article}
\usepackage{etoolbox}% http://ctan.org/pkg/etoolbox
\makeatletter
\newif\ifprinted
\newcounter{select@i}
\newcommand\select[2]{% \select{<num>}{<csv list>}
\printedfalse% Nothing has been printed
\setcounter{select@i}{0}% List starts from 1
\renewcommand*{\do}[1]{%
\stepcounter{select@i}% Next item
\ifnum\value{select@i}=#1\relax##1\printedtrue\fi}% How each item should be processed
\docsvlist{#2}% Process list
\ifprinted\else??\fi% No number was printed
}
\makeatother
\begin{document}
\begin{itemize}
\item \verb+\select{0}{A,B,C,D,E}+ gives \select{0}{A,B,C,D,E}
\item \verb+\select{1}{A,B,C,D,E}+ gives \select{1}{A,B,C,D,E}
\item \verb+\select{2}{A,B,C,D,E}+ gives \select{2}{A,B,C,D,E}
\item \verb+\select{5}{A,B,C,D,E}+ gives \select{5}{A,B,C,D,E}
\item \verb+\select{10}{A,B,C,D,E}+ gives \select{10}{A,B,C,D,E}
\item \verb+\select{1}{5,4,3,2,1}+ gives \select{1}{5,4,3,2,1}
\item \verb+\select{2}{5,4,3,2,1}+ gives \select{2}{5,4,3,2,1}
\end{itemize}
\end{document}
无需计算列表中的项目数。仅??
当\printedfalse
(未打印任何数字)时才打印出来。
答案3
我建议使用提供此功能的 l3clist \clist_item:Nn
。列表从添加索引 1 开始。
\documentclass{article}
\usepackage{biblatex}
\usepackage{xparse}
\ExplSyntaxOn
\NewDocumentCommand \select { m m }
{
\clist_item:nn { #2 } { #1 + 1 }
}
\ExplSyntaxOff
\begin{document}
\begin{itemize}
\item \verb+\select{0}{A,B,C,D,E}+ gives \select{0}{A,B,C,D,E}
\item \verb+\select{1}{A,B,C,D,E}+ gives \select{1}{A,B,C,D,E}
\item \verb+\select{2}{A,B,C,D,E}+ gives \select{2}{A,B,C,D,E}
\item \verb+\select{5}{A,B,C,D,E}+ gives \select{5}{A,B,C,D,E}
\item \verb+\select{10}{A,B,C,D,E}+ gives \select{10}{A,B,C,D,E}
%these do not work
\item \verb+\select{1}{1,2,3,4,5}+ gives \select{1}{1,2,3,4,5}
\item \verb+\select{2}{1,2,3,4,5}+ gives \select{2}{1,2,3,4,5}
\end{itemize}
\end{document}
答案4
您可以使用以下宏新工具以可扩展的方式实现此目的。
\documentclass{article}
\usepackage{xinttools}% \http://ctan.org/pkg/xint
% if #1 is too big, returns nothing; if #1 is < 0, returns from the tail
% if #1=0 returns the number of items
\newcommand{\select}[2]{\xintNthElt {#1}{\xintCSVtoList{#2}}}
\begin{document}\thispagestyle{empty}
\begin{itemize}
\item \verb+\select{0}{A,B,C,D,E}+ gives \select{0}{A,B,C,D,E}
\item \verb+\select{1}{A,B,C,D,E}+ gives \select{1}{A,B,C,D,E}
\item \verb+\select{2}{A,B,C,D,E}+ gives \select{2}{A,B,C,D,E}
\item \verb+\select{5}{A,B,C,D,E}+ gives \select{5}{A,B,C,D,E}
\item \verb+\select{10}{A,B,C,D,E}+ gives \select{10}{A,B,C,D,E}
% these do work
\item \verb+\select{1}{1,2,3,4,5}+ gives \select{1}{1,2,3,4,5}
\item \verb+\select{2}{1,2,3,4,5}+ gives \select{2}{1,2,3,4,5}
\end{itemize}
\end{document}