xfor 循环因数值参数失败的结果

xfor 循环因数值参数失败的结果

我正在尝试编写一个宏,从逗号分隔的列表中选择一个项目, \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@item1很容易预测混乱。更好的风格是

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

选择

相关内容