我正在尝试创建一个使用两个参数的完全可扩展函数。第一个参数用于tokenlist
测试第二个参数 another tokenlist
。
测试是逐个字符的比较(我知道正则表达式是更好的解决方案,事实上也许纯 TeX 也很好),但我正在尝试熟悉 LaTeX3。
你会如何改进代码?经过更多测试的代码可用于检查数字、元音、有效字符输入等。
\documentclass{article}
\usepackage{expl3,xcolor}
\begin{document}
\ExplSyntaxOn
\def\PASS{\par{\bfseries\textcolor{green!80!blue}{PASS\ }}}
\def\FAIL{\par{\bfseries\textcolor{red!70!black}{FAIL\ }}}
\cs_new:Npn \test_two:nn #1#2 {
% syntactic cyanide for expandafters
\cs_generate_variant:Nn \tl_if_in:NnTF {ff }
\cs_generate_variant:Nn \cs_gset:Npn {Npf}
\cs_generate_variant:Nn \tl_set_eq:NN {Nn}
\cs_generate_variant:Nn \int_step_inline:nnnn {nnfn, nnVn}
\cs_generate_variant:Nn \int_set_eq:NN {No,Nf}
\cs_generate_variant:Nn \tl_gset:Nn {No,Nf}
% set the lists
\tl_gset:Nx \temp {#2}
\tl_gset:Nx \temp_needle_tl {#1}
\tl_gset:Nn \head_tl {\tl_head:V \temp_needle_tl }
\tl_gset:Nn \start_tl {\temp_needle_tl }
% print some values
head~at~start~ \head_tl\par
tail~at~ start~ \start_tl\ par
% set iteration limit
\int_set_eq:Nf \g_tmpa_int {\tl_count:V\temp_needle_tl}
Number~of~items~to~test \int_use:N \g_tmpa_int\par
% iteration
\int_step_inline:nnnn {1} {1} {\g_tmpa_int}{
% test value
\tl_if_in:ffTF {\temp} {\head_tl}
{
\PASS \head_tl\ ~~\start_tl \par
}
{
\FAIL \head_tl\ ~~\start_tl \par
}
% Swap and go
\tl_gset:Nn \head_tl {\tl_head:f\start_tl }
\tl_gset:Nf \oldtail_tl {\start_tl }
\tl_gset:Nn \start_tl {\tl_tail:f \oldtail_tl}
}
}
\test_two:nn {1234567890AAA}{-1234567890)(}
\test_two:nn {apple}{aeiouAEIOU)(}
\end{document}
编辑:最初我要求更正一些错误,我已经修复了。现在我要求改进代码。
答案1
正如评论中所述,如果您想要一个可扩展的功能,那么不能使用任何其中有不可扩展的函数。此外,问题中还有各种奇怪/错误的参数类型分配。但是,整个过程可以更紧凑地完成,至少如果我们可以假设我们不必担心空格、括号组或类似的东西。
\documentclass{article}
\usepackage{expl3,xcolor}
\begin{document}
\def\PASS{\par{\bfseries\textcolor{green!80!blue}{PASS\ }}}
\def\FAIL{\par{\bfseries\textcolor{red!70!black}{FAIL\ }}}
\ExplSyntaxOn
\cs_new:Npn \ylcompare #1#2
{
\__yl_compare_auxi:nN {#2} #1 \q_recursion_tail \q_recursion_stop
}
\cs_new:Npn \__yl_compare_auxi:nN #1#2
{
\quark_if_recursion_tail_stop:N #2
\__yl_compare_auxii:nN {#1} #2
\__yl_compare_auxi:nN {#1}
}
\cs_new:Npn \__yl_compare_auxii:nN #1#2
{
\__yl_compare_auxiii:NN #2 #1 \q_recursion_tail \q_recursion_stop
}
\cs_new:Npn \__yl_compare_auxiii:NN #1#2
{
\quark_if_recursion_tail_stop_do:Nn #2 { \FAIL #1 }
\str_if_eq:nnT {#1} {#2}
{
\use_i_delimit_by_q_recursion_stop:nw { \PASS #1 }
}
\__yl_compare_auxiii:NN #1
}
\ExplSyntaxOff
\ylcompare{1234567890AAA}{-1234567890)(}
\ylcompare{apple}{aeiouAEIOU)(}
\end{document}
这里的想法是针对要检查的列表设置一个可扩展的递归,然后针对“允许”列表启动第二个递归。这使用了 等提供\q_recursion_tail
并在 中讨论的通用递归思想interface3
。
如果需要涵盖更复杂的情况,可以使用“令牌列表操作”方法的实现,例如用于可扩展案例的变化。
对于检查整数值(或实际上是实数)的情况,我会对各种子循环进行硬编码。(事实上,除非绝对需要,否则我可能会以非扩展的方式执行此操作。例如,siunitx
测试有效输入但不通过扩展来执行此操作,因为它进行排版,而l3fp
进行基于扩展的值检查,因为它需要可扩展。)假设我们坚持采用expl3
可扩展的方法,可以这样做
\documentclass{article}
\usepackage{expl3}
\begin{document}
\ExplSyntaxOn
\cs_new:Npn \ylinttest #1
{ \__yl_compare_auxi:N #1 \q_recursion_tail \q_recursion_stop }
\cs_new:Npn \__yl_compare_auxi:N #1
{
\quark_if_recursion_tail_stop_do:Nn #1 { FAIL }
\bool_if:nTF
{
\str_if_eq_p:nn {#1} { + }
|| \str_if_eq_p:nn {#1} { - }
}
{ \__yl_compare_auxi:N }
{ \__yl_compare_auxii:N #1 }
}
\cs_new:Npn \__yl_compare_auxii:N #1
{
\quark_if_recursion_tail_stop_do:Nn #1 { PASS }
\__yl_compare_auxiii:N #1
\__yl_compare_auxii:N
}
\cs_new:Npn \__yl_compare_auxiii:N #1
{
\__yl_compare_auxiv:NN #1 0123456789 \q_recursion_tail \q_recursion_stop
}
\cs_new:Npn \__yl_compare_auxiv:NN #1#2
{
\quark_if_recursion_tail_stop_do:Nn #2
{ \use_i_delimit_by_q_recursion_stop:nw { FAIL } }
\str_if_eq:nnT {#1} {#2}
{ \use_none_delimit_by_q_recursion_stop:w }
\__yl_compare_auxiv:NN #1
}
\ExplSyntaxOff
\ylinttest{1234567890}
\ylinttest{---12345}
\ylinttest{---1-2345}
\ylinttest{12345A}
\ylinttest{}
\end{document}
这里的想法是,有效整数可以有一个或多个,+
或者-
在开头后跟至少一个数值。通过将循环拆分为几部分,无需尝试跟踪状态。在实际情况下,您可能会在测试后有两条代码路径,而不是PASS
/ ,并且/可以选择合适的代码路径。FAIL
\use_i:n
\use_ii:nn
可以使用相同的方法测试浮点数,但需要更多循环。我可能会看一下 Brunol3fp
为此编写的版本:它运行良好且经过全面测试,因此是一个很好的起点。