我想要一个执行以下操作的命令:
\variation{1}{aaa;bbb;ccc} ----> aaa
\variation{2}{aaa;bbb;ccc} ----> bbb
\variation{3}{aaa;bbb;ccc} ----> ccc
第二个输入中的字符串可以是包含任意多个 ; 的列表(可能根本没有),而不仅仅是如图所示的三个。我几乎已经使用 xstring 得到了我需要的东西,但它包含多余的输入和符号,基本上不够优雅!
\newcommand{\variation}[3]{\StrBetween[#1,#2]{;#3;}{;}{;}}
因此这就是我得到的
\variation{1}{2}{aaa;bbb;ccc} ----> aaa
第二个输入是多余的。
答案1
xstring
和 eTeX 的解决方案\numexpr
:
\documentclass{article}
\usepackage{xstring}
\newcommand*{\variation}[2]{%
\StrBetween[{#1},{\numexpr(#1)+1\relax}]{;#2;}{;}{;}%
}
\newcommand*{\test}[1]{%
\detokenize{#1} $\rightarrow$ #1%
}
\begin{document}
\noindent
\ttfamily
\test{\variation{1}{aaa;bbb;ccc}}\\
\test{\variation{2}{aaa;bbb;ccc}}\\
\test{\variation{3}{aaa;bbb;ccc}}
\end{document}
更新
\StrBetween 知道最后一个可选参数,允许将结果存储在宏中。因为我不知道如何
\variation
使用,所以宏继承了最后一个可选参数。在 LaTeX 中查找可选参数(基于\@ifnextchar
)通常会删除后面的空格以找到开头的方括号。\relax
停止查找并 保留[
参数括号后的空格。\variation
注释中的第二个问题:软件包
xstring
在数字比较后添加了空格(未注释的行尾)。如果是显式数字,则忽略空格并完成数字的解析。但如果数字由整数寄存器给出,或者在本例中由完整的给出\numexpr
,则不需要空格并保留空格。有几种方法可以解决此问题:
- 错误报告。
\the\numexpr
代替\numexpr
- 展开参数并转换为显式数字。然后
\StrBetween
只能看到展开的数字。
新的测试文件:
\documentclass{article}
\usepackage{xstring}
\newcommand*{\variation}[2]{%
\edef\variationNext{%
\noexpand\StrBetween[%
\the\numexpr(#1)\relax,%
\the\numexpr(#1)+1\relax
]%
}%
\variationNext{;#2;}{;}{;}\relax
}
\newcommand*{\test}[1]{%
\detokenize\expandafter{\string#1} $\rightarrow$ [#1]%
}
\begin{document}
\noindent
\ttfamily
\test{\variation{1}{aaa;bbb;ccc}}\\
\test{\variation{2}{aaa;bbb;ccc}}\\
\test{\variation{3}{aaa;bbb;ccc}}
\end{document}
答案2
这里有一个使用xparse/expl3
而不是 的选项xstring
(如果您只对解决方案感兴趣xstring
,那么就将其作为比较)。该variation
宏采用可选的第一个参数来确定要拆分列表的字符,默认值为;
。
\documentclass{article}
\usepackage{xparse}
\ExplSyntaxOn
\msg_new:nnn {variation}{index~out~of~bounds}{You~have~requested~an~item~that~doesn't~exist}
\NewDocumentCommand{\variation}{ O{;} m m }
{
% split arg #3 at every occurrence of #1 (default #1=;) and store in sequence
\seq_set_split:Nnn \l_tmpa_seq {#1}{#3}
% compare arg #2 to the length of the sequence
\int_compare:nTF {#2>\seq_count:N \l_tmpa_seq}
% if arg #2 > sequence length, then the item doesn't exist
{\msg_warning:nnn {variation}{index~out~of~bounds}{}}
% if arg #2 <= sequence length, then return requested item.
{\seq_item:Nn \l_tmpa_seq {#2}}
}
\ExplSyntaxOff
\begin{document}
\variation{3}{aaa;bbb;ccc;ddd;eee}
\variation{1}{aaa}
\variation{2}{aaa}
\end{document}
答案3
这只是 Scott H. 解决方案的一个变体,用于展示一种更好的解决问题的方法。
\documentclass{article}
\usepackage{xparse}
\ExplSyntaxOn
\NewDocumentCommand{\variation}{ s O{;} m m }
{
\IfBooleanTF{#1}
{
\geoff_variation_check:nnn { #2 } { #3 } { #4 }
}
{
\geoff_variation_nocheck:nnn { #2 } { #3 } { #4 }
}
}
\msg_new:nnn { variation }
{ index~out~of~bounds }
{ You~have~requested~an~item~that~doesn't~exist }
\seq_new:N \l__geoff_item_list_seq
\cs_new_protected:Npn \geoff_variation_nocheck:nnn #1 #2 #3
{
\seq_set_split:Nnn \l__geoff_item_list_seq { #1 } { #3 }
\seq_item:Nn \l__geoff_item_list_seq { #2 }
}
\cs_new_protected:Npn \geoff_variation_check:nnn #1 #2 #3
{
\seq_set_split:Nnn \l__geoff_item_list_seq { #1 } { #3 }
\int_compare:nTF { #2 > \seq_count:N \l__geoff_item_list_seq }
{ \msg_warning:nn { variation }{ index~out~of~bounds } }
{ \seq_item:Nn \l__geoff_item_list_seq {#2} }
}
\ExplSyntaxOff
\begin{document}
\variation{3}{aaa;bbb;ccc;ddd;eee}
\variation[,]{3}{aaa,bbb,ccc,ddd,eee}
\variation{1}{aaa}
\variation{2}{aaa}
\variation*{2}{aaa}
\end{document}
指令\NewDocumentCommand
应该用于将参数传递给“内部”函数。为了更好地说明如何操作,我引入了一个 *-variant,因此控制权要么传递给 ,要么\geoff_variation_check:nnn
传递给\geoff_variation_nocheck:nnn
。内部函数应该是protected
,因为它们执行不可扩展的操作(\set...
)。
大部分操作是将参数拆分为一个序列并返回所需的序列项:
\seq_set_split:Nnn <sequence> { <tokens> } { <token list> }
\seq_item:Nn <sequence> { <integer expression> }
答案4
在一般情况下,机器可能会生成如下列表
{aaa;bbb; ; ccc}
这将产生意想不到的结果
\test{\variation{3}{aaa;bbb; ; ccc}} -> [ ]
此外,解析器(;
在本例中)可能处于活动状态。这些要求事先对列表进行规范化。
\documentclass{article}
\usepackage{catoptions}
\makeatletter
% \getstrofnumber{<parser>}{<nr>}{<list>}
\robust@def*\getstrofnumber#1#2#3{%
\begingroup
\@tempcnta\z@
\def\@getstrofnumber##1#1##2\get@nnil{%
\ifstrcmpTF{##1}\get@nil{%
\endgroup
\@latexerr{Item of serial number '#2' not found}\@ehd
}{%
\advance\@tempcnta\@ne
\ifnum#2=\@tempcnta
\endgroup##1%
\expandafter\@gobble
\else
\expandafter\@iden
\fi
{\@getstrofnumber##2\get@nnil}%
}%
}%
\edef\alist{\unexpanded{#3}}%
\cpt@sttrue\cpt@csvnormalize[#1]\alist
\expandafter\@getstrofnumber\alist#1\get@nil#1\get@nnil
}
\makeatother
\begin{document}
\def\pput#1{\char`{#1\char`} \space$\rightarrow$\space}
\noindent
\ttfamily
\pput{aaa;bbb; ; ccc}\getstrofnumber{;}{1}{aaa;bbb; ; ccc}\\
\pput{aaa;;bbb;ccc}\getstrofnumber{;}{2}{aaa;;bbb;ccc}\\
\pput{aaa;bbb;ccc}\getstrofnumber{;}{3}{aaa;bbb;ccc}\\
% \pput{aaa;bbb;ccc}\getstrofnumber{;}{4}{aaa;bbb;ccc}% error
\end{document}