我想知道是否有一种简单的方法可以将一些输入文本(例如“3-7”)扩展为“3,4,5,6,7”?
就上下文而言,我正在使用\foreach
并希望能够编写
\foreach \x in {3-7, 9, 14, 52}
并已\x
运行至3、4、5、6、7、9、14和52。
或者,如果有一个简单的方法
\foreach \x in {1,3,...7}
导致\x
被定义为“1、3、4、5、6 和 7”而不是标准的“1、3、5 和 7”,那就太好了。
编辑:我认为我的解释并不清楚,但我个人了解如何\foreach
在 TeX 中执行这些命令,但希望有一个简单的输入结构供非 TeX 用户使用,即输入\makeProblems{1, 3-7, 9, 14, 52}
并将相关问题作为输出。
答案1
只需编写少量代码,您就可以制作一个解析器。我\makeProblems{<integer list>}{<code>}
为您定义了一个,其中<integer list>
是一个以逗号分隔的数字列表,其中被解析为介于和<x>-<y>
之间的整数列表(包括)。该函数解析数字列表,然后遍历生成的列表,并使当前数字可用为。例如:<x>
<y>
<code>
#1
\makeProblems{1,3-7, 9, 14, 52}{Do something with #1.\par}
印刷:
代码很长,因为当函数接受用户输入时,函数会格外小心以确保<integer list>
不包含错误输入。
\documentclass{article}
\usepackage{xparse}
\ExplSyntaxOn
\tl_new:N \l_ryanj_list_tl
\NewDocumentCommand \makeProblems { m +m }
{
\tl_clear:N \l_ryanj_list_tl
\exp_args:Nx \clist_map_function:nN {#1} \__ryanj_parse_item:n
\tl_map_inline:Nn \l_ryanj_list_tl {#2}
}
\cs_new_protected:Npn \__ryanj_add_item:n #1
{ \tl_put_right:Nn \l_ryanj_list_tl { {#1} } }
\cs_new_protected:Npn \__ryanj_parse_item:n #1
{
\__ryanj_if_number:nTF {#1}
{ \__ryanj_add_item:n {#1} }
{
\str_if_in:nnTF {#1} {-}
{ \exp_args:Nf \__ryanj_parse_range:n { \tl_to_str:n {#1} } }
{ \msg_error:nnn { ryanj } { invalid-number } {#1} }
}
}
\cs_new_protected:Npn \__ryanj_parse_range:n #1
{ \__ryanj_parse_range:nw {#1} #1 \q_mark }
\cs_new_protected:Npn \__ryanj_parse_range:nw #1#2-#3 \q_mark
{
\__ryanj_validate_number:nn {#1} {#2}
\__ryanj_validate_number:nn {#1} {#3}
\int_step_function:nnnN {#2} { 1 } {#3} \__ryanj_add_item:n
\use_none:n \q_stop
}
\cs_new_protected:Npn \__ryanj_validate_number:nn #1 #2
{
\__ryanj_if_number:nF {#2}
{
\msg_error:nnnn { ryanj } { invalid-number-in-range } {#2} {#1}
\use_none_delimit_by_q_stop:w
}
}
\msg_new:nnn { ryanj } { invalid-range } { Invalid~range~`#1'. }
\msg_new:nnn { ryanj } { invalid-number } { Invalid~number~`#1'. }
\msg_new:nnn { ryanj } { invalid-number-in-range } { Invalid~number~`#1'~in~range~`#2'. }
\prg_new_conditional:Npnn \__ryanj_if_number:n #1 { T, F, TF }
{
\tl_if_empty:oTF
{ \tex_romannumeral:D - 0#1 \exp_stop_f: }
{
\tl_if_empty:nTF {#1}
{ \prg_return_false: }
{ \prg_return_true: }
}
{ \prg_return_false: }
}
% For older expl3:
\prg_set_protected_conditional:Npnn \str_if_in:nn #1#2 { T , F , TF }
{
\use:x
{ \tl_if_in:nnTF { \tl_to_str:n {#1} } { \tl_to_str:n {#2} } }
{ \prg_return_true: } { \prg_return_false: }
}
\ExplSyntaxOff
\begin{document}
\makeProblems{1,3-7, 9, 14, 52}{Do something with #1.\par}
\end{document}
对于挑剔的哺乳动物,这里有一个理解负数的版本。负数可以自然地用符号输入,例如-4
。在数字范围内,第一个-
数字后面的第一个被解析为范围指示符,而不是符号,因此1-3
是1,2,3
、1--3
是1,0,-1,-2,-3
、-1-3
是-1,0,1,2,3
、-1--3
是-1,-2,-3
和-1---3--
是胡言乱语:-)
我不建议使用这种语法,因为它可能会有点令人困惑。我会选择另一个字符来表示范围,然后负数“就可以正常工作”。我还启用了反向范围,因此1-3
收益1,2,3
和3-1
收益3,2,1
。
\documentclass{article}
\usepackage{xparse}
\ExplSyntaxOn
\tl_new:N \l_ryanj_list_tl
\tl_new:N \l_ryanj_sign_tl
\tl_new:N \l_ryanj_parsing_tl
\bool_new:N \l_ryanj_first_bool
\bool_new:N \l_got_range_bool
\bool_new:N \l_ryanj_ranged_bool
\int_new:N \l_ryanj_rangea_int
\int_new:N \l_ryanj_rangeb_int
\NewDocumentCommand \makeProblems { m +m }
{
\tl_clear:N \l_ryanj_list_tl
\exp_args:Nx \clist_map_function:nN {#1} \__ryanj_parse_item:n
\tl_map_inline:Nn \l_ryanj_list_tl {#2}
}
\cs_new_protected:Npn \__ryanj_add_item:n #1
{ \tl_put_right:Nn \l_ryanj_list_tl { {#1} } }
\cs_generate_variant:Nn \__ryanj_add_item:n { V }
\cs_new_protected:Npn \__ryanj_parse_item:n #1
{
\tl_clear:N \l_ryanj_sign_tl
\bool_set_true:N \l_ryanj_first_bool
\bool_set_false:N \l_ryanj_ranged_bool
\bool_set_false:N \l_got_range_bool
\tl_set:Nn \l_ryanj_parsing_tl {#1}
\__ryanj_parse:w #1 ~ \q_recursion_tail \q_recursion_stop
}
\cs_new_protected:Npn \__ryanj_parse:w #1
{
\tl_if_single_token:nF {#1} { \__ryanj_abort_item:nnw { braced-item } {#1} }
\quark_if_recursion_tail_stop_do:Nn #1
{ \__ryanj_parse_terminate: }
\token_if_eq_charcode:NNTF - #1
{
\bool_if:NTF \l_ryanj_first_bool
{ \tl_set:Nn \l_ryanj_sign_tl { - } }
{
\bool_if:NTF \l_got_range_bool
{ \tl_set:Nn \l_ryanj_sign_tl { - } }
{ \bool_set_true:N \l_got_range_bool }
}
\__ryanj_parse:w
}
{ \__ryanj_grab_number:w #1 }
}
\cs_new_protected:Npn \__ryanj_grab_number:w #1
{
\quark_if_recursion_tail_stop_do:nn {#1} { \msg_error:nnn { ryanj } { invalid-number } {} }
\__ryanj_if_number:nF {#1} { \__ryanj_abort_item:nnw { invalid-number } {#1} }
\bool_if:NTF \l_ryanj_first_bool
{ \tex_afterassignment:D \__ryanj_after_first: \l_ryanj_rangea_int }
{ \tex_afterassignment:D \__ryanj_after_second: \l_ryanj_rangeb_int }
= \l_ryanj_sign_tl #1
}
\cs_new_protected:Npn \__ryanj_after_first:
{
\bool_set_false:N \l_ryanj_first_bool
\tl_clear:N \l_ryanj_sign_tl
\__ryanj_parse:w
}
\cs_new_protected:Npn \__ryanj_after_second:
{
\bool_set_true:N \l_ryanj_ranged_bool
\bool_set_false:N \l_got_range_bool
\tl_clear:N \l_ryanj_sign_tl
\__ryanj_parse_terminate:w
}
\cs_new_protected:Npn \__ryanj_parse_terminate:w #1 \q_recursion_stop
{ \tl_trim_spaces_apply:nN {#1} \__ryanj_ckeck_leftover:n }
\cs_new_protected:Npn \__ryanj_ckeck_leftover:n #1
{
\quark_if_recursion_tail_stop:n {#1}
\msg_error:nnn { ryanj } { invalid-number } {#1}
\use_none:n \q_recursion_stop
\__ryanj_parse_terminate:
}
\cs_new_protected:Npn \__ryanj_parse_terminate:
{
\bool_if:NT \l_got_range_bool
{ \msg_error:nnn { ryanj } { invalid-number } { - } }
\bool_if:NTF \l_ryanj_ranged_bool
{ \__ryanj_inject_range: }
{ \__ryanj_add_item:V \l_ryanj_rangea_int }
}
\cs_new:Npn \__ryanj_inject_range:
{
% To allow reversed ranges:
\int_compare:nNnT \l_ryanj_rangea_int > \l_ryanj_rangeb_int
{ \tl_set:Nn \l_ryanj_sign_tl { - } }
{ \tl_set:Nn \l_ryanj_sign_tl { } }
%
\int_step_function:nnnN
{ \l_ryanj_rangea_int }
{ \l_ryanj_sign_tl 1 }
{ \l_ryanj_rangeb_int }
\__ryanj_add_item:n
}
\cs_new:Npn \__ryanj_abort_item:nnw #1 #2 #3 \q_recursion_stop
{ \msg_error:nnn { ryanj } {#1} {#2} }
\prg_new_conditional:Npnn \__ryanj_if_number:n #1 { T, F, TF }
{
\tl_if_empty:oTF
{ \tex_romannumeral:D - 0#1 \exp_stop_f: }
{ \prg_return_true: }
{ \prg_return_false: }
}
\msg_new:nnn { ryanj } { invalid-number }
{ Invalid~number~`#1'~in~\tl_use:N \l_ryanj_parsing_tl. }
\msg_new:nnn { ryanj } { braced-item }
{ Invalid~braced~item~`#1'~in~\tl_use:N \l_ryanj_parsing_tl. }
\ExplSyntaxOff
\begin{document}
\makeProblems{-1,3-5,-3--5,-3-5,3--5,9,14,52}{Do something with #1.\par}
\end{document}
答案2
\documentclass{article}
\usepackage{listofitems,pgffor}
\newcommand\makeProblems[2]{%
\setsepchar{,/-}%
\readlist*\numlist{#1}%
\def\z##1{#2\par}%
\foreachitem\zz\in\numlist[]{%
\ifnum\listlen\numlist[\zzcnt]=1\relax\z{\zz}\else
\itemtomacro\numlist[\zzcnt,1]\tmpA
\itemtomacro\numlist[\zzcnt,2]\tmpB
\foreach\zzz in {\tmpA,...,\tmpB}{%
\z{\zzz}}%
\fi
}%
}
\begin{document}
\makeProblems{1,3-7, 9, 14-16, 52}{Do something with #1.}
\end{document}
答案3
我映射给定的逗号分隔列表;检查每个项目,如果它包含连字符,则进行循环;无论如何,都会将整数添加到序列中。
最后,使用项目之间的分隔符扩展序列;可选择将此标记列表保存到宏中。
\documentclass{article}
\usepackage{xparse}
\ExplSyntaxOn
\NewDocumentCommand{\expandlist}{om}
{
\ryanj_expandlist:n { #2 }
\IfNoValueTF { #1 }
{
\ryanj_expandlist_print:
}
{
\ryanj_expandlist_store:N #1
}
}
\tl_new:N \l_ryan_expandlist_tl
\seq_new:N \l__ryan_expandlist_seq
\cs_new_protected:Nn \ryanj_expandlist:n
{
\seq_clear:N \l__ryan_expandlist_seq
\clist_map_function:nN { #1 } \__ryan_expandlist_item:n
\tl_set:Nx \l_ryan_expandlist_tl
{
\seq_use:Nnnn \l__ryan_expandlist_seq {~and~} { ,~ } { ,~and~ }
}
}
\cs_new_protected:Nn \__ryan_expandlist_item:n
{
\__ryan_expandlist_item:w #1 - - \q_stop
}
\cs_new_protected:Npn \__ryan_expandlist_item:w #1 - #2 - #3 \q_stop
{
\tl_if_blank:nTF { #2 }
{
\seq_put_right:Nn \l__ryan_expandlist_seq { #1 }
}
{
\int_step_inline:nnn { #1 } { #2 } { \seq_put_right:Nn \l__ryan_expandlist_seq { ##1 } }
}
}
\cs_new:Nn \ryanj_expandlist_print:
{
\tl_use:N \l_ryan_expandlist_tl
}
\cs_new_protected:Nn \ryanj_expandlist_store:N
{
\tl_if_exist:NF #1
{
\tl_set_eq:NN #1 \l_ryan_expandlist_tl
}
}
\ExplSyntaxOff
\begin{document}
\expandlist{3-7, 9, 14, 52}
\expandlist{1}
\expandlist{1,4}
\expandlist{1-2}
\expandlist[\foo]{3-7, 9, 14, 52}
\texttt{\meaning\foo}
\end{document}