![使用两个逗号列表构建一个使用 \str_case:nn 的测试函数](https://linux22.com/image/467122/%E4%BD%BF%E7%94%A8%E4%B8%A4%E4%B8%AA%E9%80%97%E5%8F%B7%E5%88%97%E8%A1%A8%E6%9E%84%E5%BB%BA%E4%B8%80%E4%B8%AA%E4%BD%BF%E7%94%A8%20%5Cstr_case%3Ann%20%E7%9A%84%E6%B5%8B%E8%AF%95%E5%87%BD%E6%95%B0.png)
假设给定两个长度相同的逗号列表:
\clist_set:Nn \l__mymodule_keys_clist { key1, key2, key3 }
\clist_set:Nn \l__mymodule_acts_clist { act1, act2, act3 }
如何将它们转换为以下形式的测试函数?
\NewDocumentCommand { \TestFunction } { m }
{
\str_case:nnF { #1 }
{
{ key1 } { act1 }
{ key2 } { act2 }
{ key3 } { act3 }
} { No ~ action! }
}
下面是 MWE。
\documentclass{article}
\begin{document}
\ExplSyntaxOn
\clist_new:N \l__mymodule_keys_clist
\clist_new:N \l__mymodule_acts_clist
\clist_set:Nn \l__mymodule_keys_clist { key1, key2, key3 }
\clist_set:Nn \l__mymodule_acts_clist { act1, act2, act3 }
\NewDocumentCommand { \TestFunction } { m }
{
\str_case:nnF { #1 }
{
{ key1 } { act1 }
{ key2 } { act2 }
{ key3 } { act3 }
} { No ~ action! }
}
\ExplSyntaxOff
\TestFunction{key2}
\TestFunction{key4}
\end{document}
预期结果是
MWE 产生了这个结果,但其内容\TestFunction
是硬编码的,并不是从给定的两个逗号列表动态创建的。
以下是我目前的尝试:
\NewDocumentCommand { \TestFunction } { m }
{
\clist_set_eq:NN \l_tmpa_clist \l__mymodule_acts_clist
\tl_clear:N \l_tmpa_tl
\clist_map_inline:Nn \l__mymodule_keys_clist
{
\tl_put_right:Nn \l_tmpa_tl { { ##1 } }
\clist_pop:NN \l_tmpa_clist \l_tmpb_tl
\exp_args:NNe \tl_put_right:Nn \l_tmpa_tl { { \l_tmpb_tl } }
}
\exp_args:Nno \str_case:nnF { #1 } { \l_tmpa_tl } { No ~ action! }
}
虽然它在这种特殊情况下有效,但问题是它使用了 -typee
扩展,这需要第二个逗号列表的内容完全可扩展,如果它包含类似 的内容,情况就不是这样\textbf
。问题是,当从第二个 弹出第一个项目时clist
,似乎我只能将其保存到宏中,比如\l_tmpb_tl
; 然后稍后,当我想附加{ \l_tmpb_tl }
到一个临时宏(这里\l_tmpa_tl
)时,o
将其扩展一次的 -type 扩展似乎不够。有什么方法可以改进这段代码,或者有其他更好的方法吗?
答案1
您似乎想将两个逗号列表变成一个属性列表(我非常热衷于相信这是一个 XY 问题)。
\documentclass{article}
\ExplSyntaxOn
\NewDocumentCommand{\makepropfromclists}{O{default}mm}
{% #1 = name of the prop, #2 = name of the keys clist, #3 = name of the actions clist
\prop_clear_new:c { l_mymodule_keysacts_#1_prop }
\int_step_inline:nn { \clist_count:c { l__mymodule_#2_clist } }
{
\prop_put:cxx { l_mymodule_keysacts_#1_prop }
{ \clist_item:cn { l__mymodule_#2_clist } { ##1 } }
{ \clist_item:cn { l__mymodule_#3_clist } { ##1 } }
}
}
\NewDocumentCommand{\TestFunction}{O{default}m}
{
\prop_if_in:cnTF { l_mymodule_keysacts_#1_prop } { #2 }
{
\prop_get:cnN { l_mymodule_keysacts_#1_prop } { #2 } \l_tmpa_tl
\tl_use:N \l_tmpa_tl
}
{ No~action! }
}
\clist_new:N \l__mymodule_keys_clist
\clist_new:N \l__mymodule_acts_clist
\clist_set:Nn \l__mymodule_keys_clist { key1, key2, key3 }
\clist_set:Nn \l__mymodule_acts_clist { act1, act2, act3 }
\makepropfromclists{keys}{acts}
\ExplSyntaxOff
\begin{document}
\TestFunction{key2}
\TestFunction{key4}
\end{document}
请勿将 clist 用于不符合其用途的事情。
答案2
您可以直接使用 TeX 原语来实现您的任务,不需要 expl3:
\def\keys {key1,key2,key3}
\def\acts {act1,act2,act3}
\def\actsX #1,#2\end#3{\expandafter\def\csname k:#3\endcsname{#1}\def\acts{#2}}
\def\setkeys #1,{\ifx\end#1\else
\expandafter\actsX \acts,\end {#1}%
\expandafter\setkeys \fi
}
\expandafter\setkeys\keys,\end,
\def\TestFunction#1{%
\ifcsname k:#1\endcsname
\csname k:#1\endcsname
\else
No action%
\fi
}
\TestFunction{key2} % prints: act2
\TestFunction{key4} % prints: No action
\setkeys
带参数的宏定义了带值的key1,key2,key3,\end,
宏、带值的宏等。如果键多于操作,则带尾键的宏为空。最后,宏打印给定键的值,如果未定义宏,则打印。\k:key1
act1
\k:key2
act2
\TestFunction
No action
答案3
使用\seq_map_pairwise_function:NNN
可以让我们达到大部分目的:
\documentclass{article}
\ExplSyntaxOn
% Starting clists
\clist_set:Nn \l__mymodule_keys_clist { key1, key2, key3 }
\clist_set:Nn \l__mymodule_acts_clist { act1, act2, act3 }
% Convert to sequences
\seq_set_from_clist:NN \l__mymodule_keys_seq \l__mymodule_keys_clist
\seq_set_from_clist:NN \l__mymodule_acts_seq \l__mymodule_acts_clist
% The value that we're searching for
\tl_new:N \l__mymodule_search_tl
% The mapping function
\cs_new_protected:Nn \__mymodule_map:nn {
\tl_if_eq:NnT \l__mymodule_search_tl { #1 } {
% \seq_map_break:n { #2 \use_none:n } % Doesn't work for some reason
\prg_break:n { #2 \use_none:n }
}
}
% The fallthrough action
\tl_const:Nn \c__mymodule_fallthrough_tl { No ~ action! }
% The user-accessible function
\NewDocumentCommand { \TestFunction } { m } {
\tl_set:Nn \l__mymodule_search_tl { #1 }
\seq_map_pairwise_function:NNN
\l__mymodule_keys_seq
\l__mymodule_acts_seq
\__mymodule_map:nn
\c__mymodule_fallthrough_tl
}
\ExplSyntaxOff
\begin{document}
\TestFunction{key2}
\TestFunction{key4}
\end{document}
如果您能够使用单个属性列表/键值表达式而不是一对 clist,那么这会更加简洁:
\documentclass{article}
\ExplSyntaxOn
% Starting property list
\prop_set_from_keyval:Nn \l__mymodule_keys_prop {
key1 = act1,
key2 = act2,
key3 = act3
}
% The user-accessible function
\NewDocumentCommand { \TestFunction } { m } {
\prop_get:NnNTF \l__mymodule_keys_prop { #1 } \l_tmpa_tl {
\tl_use:N \l_tmpa_tl
} {
No ~ action!
}
}
\ExplSyntaxOff
\begin{document}
\TestFunction{key2}
\TestFunction{key4}
\end{document}
两种方法的输出相同:
答案4
尽管 expl3 的映射函数似乎不是为对标记列表进行“就地”操作而设计的,但接下来我假设正在使用的 LaTeX 发行版足够新,可以\clist_map_function:NN
使用。(我的 TeX 发行版的 interface3.pdf 说大约是\clist_map_function:NN
:Updated: 2012-06-29
。这是大约 11 年前的事了,因此假设可用\clist_map_function:NN
可能是合理的。)
以下所有代码的要点是应用\clist_map_function:NN
逗号列表项映射到函数/宏,该函数/宏使用参数分隔符将嵌套在分隔符后面的括号中的项放置。在分隔符之前,您会找到括号中的逗号列表项和形成映射函数的下一个迭代的标记。
因此,在每次映射迭代之后,您都会得到类似
\function{⟨item in this mapping-iteration⟩}⟨stuff for subsequent mapping-iterations⟩\DELIMITER{⟨item from last mapping-iteration⟩}{⟨item from last but one mapping-iteration⟩}..{⟨item from first mapping-iteration⟩}
。
并\function
使用参数#1
和#2\DELIMITER
并执行:
⟨stuff for subsequent mapping-iterations⟩\DELIMITER{⟨item in this mapping-iteration⟩}{⟨item from last mapping-iteration⟩}{⟨item from last but one mapping-iteration⟩}..{⟨item from first mapping-iteration⟩}
。
所以不久之后从items,of,list,a
和ITEMS,OF,LIST,B
,诸如此类
\DELIMITER{⟨result gathered so far, initially empty⟩}{B}{LIST}{OF}{ITEMS}{},{a}{list}{of}{items}{},
就形成了。
这反过来可以通过使用循环来合并,其中\DELIMITER
用参数模式#1#2#3,#4#5,
→ 定义\DELIMITER{{#2}{#4}#1}#3,#5,
,并且当为空参数时终止,{#3#5}
从而{#2}{#4}#1
形成合并结果。
警告1:
⟨stuff for subsequent mapping-iterations⟩
\DELIMITER
除非嵌套在花括号中,否则不得包含。
我没有费心检查的实现,\clist_map_function:NN
以查明是否可能在花括号外⟨stuff for subsequent mapping-iterations⟩
包含。\DELIMITER
警告2:
应用映射到单个逗号列表项的函数会导致输入堆栈中的内容累积。因此,对于(不是那么)大的逗号列表,您可能会收到类似以下错误消息:
! TeX capacity exceeded, sorry [input stack size=...].
在下面的代码中,\mymodule_mergecommalists:nnwnw
用来代替\DELIMITER
并且\mymodule_moveargbehindmergecommalistsbraced:nw
用来代替\function
/映射到逗号列表的项目。
\l__mymodule_keys_clist
如果您希望将和的内容\l__mymodule_acts_clist
硬编码到的定义中\TestFunction
,以便的行为\TestFunction
不受\l__mymodule_keys_clist
和/或的后续更改的影响\l__mymodule_acts_clist
,您可以这样做。
\documentclass{article}
\ExplSyntaxOn
% \DefineTestFunction{<control word token denoting test function to define>}%
% {<additional xparse-argument-specifiers, denoting #2, #3 etc>
% % #1 is available in <action if #1 is not a key of the keys comma list>
% % in any case and denotes the argument to test
% }%
% {<keys comma list>}%
% {<actions comma list>}%
% {<action if #1 is not a key of the keys comma list>}%
\cs_new:Npn \mymodule_DefineTestFunction:NnNNn #1#2#3#4#5 {
\exp_args:Nno \use:n
{ \NewDocumentCommand { #1 } { m#2 } } {
\exp:w \exp_args:Nno \use:n {\exp_end: \str_case:nnF {##1} }
{%
\exp:w
\mymodule_moveargbehindmergecommalistsunbraced:nw{{},}
\clist_map_function:NN #4 \mymodule_moveargbehindmergecommalistsbraced:nw
\mymodule_moveargbehindmergecommalistsunbraced:nw{{},}
\clist_map_function:NN #3 \mymodule_moveargbehindmergecommalistsbraced:nw
\mymodule_moveargbehindmergecommalistsbraced:nw{}
\mymodule_mergecommalists:nnwnw
}{#5}
}
}
\cs_new:Npn \mymodule_moveargbehindmergecommalistsunbraced:nw #1#2\mymodule_mergecommalists:nnwnw
{#2 \mymodule_mergecommalists:nnwnw #1}
\cs_new:Npn \mymodule_moveargbehindmergecommalistsbraced:nw #1#2\mymodule_mergecommalists:nnwnw
{#2 \mymodule_mergecommalists:nnwnw {#1}}
\cs_new:Npn \mymodule_mergecommalists:nnwnw #1#2#3,#4#5,
{\tl_if_empty:nTF{#3#5}{\exp_end:{#2}{#4}#1}{\mymodule_mergecommalists:nnwnw {{#2}{#4}#1}#3,#5,}}
%-----------------------------------------------------------------------------------
\clist_set:Nn \l__mymodule_keys_clist { key1, key2, key3 }
\clist_set:Nn \l__mymodule_acts_clist { act1, act2, act3 }
\mymodule_DefineTestFunction:NnNNn{\TestFunction}{}
{\l__mymodule_keys_clist}
{\l__mymodule_acts_clist}
{The~key~#1~is~not~in~the~list,~thus~default~action!}
\mymodule_DefineTestFunction:NnNNn{\OtherTestFunction}{m}
{\l__mymodule_keys_clist}
{\l__mymodule_acts_clist}
{The~key~#1~is~not~in~the~list,~thus:~#2}
\cs_show:c{TestFunction~code}
\cs_show:c{OtherTestFunction~code}
\ExplSyntaxOff
\begin{document}
\TestFunction{key1}
\TestFunction{key2}
\TestFunction{key3}
\TestFunction{key4}
\OtherTestFunction{key1}{This time's default}
\OtherTestFunction{key2}{This time's default}
\OtherTestFunction{key3}{This time's default}
\OtherTestFunction{key4}{This time's default}
\end{document}
如果您希望在执行时对列表\l__mymodule_keys_clist
进行评估,您可以这样做。像这样:\l__mymodule_acts_clist
\TestFunction
\documentclass{article}
\ExplSyntaxOn
% \DefineTestFunction{<control word token denoting test function to define>}%
% {<additional xparse-argument-specifiers, denoting #2, #3 etc>
% % #1 is available in <action if #1 is not a key of the keys comma list>
% % in any case and denotes the argument to test
% }%
% {<keys comma list>}%
% {<actions comma list>}%
% {<action if #1 is not a key of the keys comma list>}%
\cs_new:Npn \mymodule_DefineTestFunction:NnNNn #1#2#3#4#5 {
\NewDocumentCommand { #1 } { m#2 } {
\exp_args:Nno \str_case:nnF {##1}
{%
\exp:w
\mymodule_moveargbehindmergecommalistsunbraced:nw{{},}
\clist_map_function:NN #4 \mymodule_moveargbehindmergecommalistsbraced:nw
\mymodule_moveargbehindmergecommalistsunbraced:nw{{},}
\clist_map_function:NN #3 \mymodule_moveargbehindmergecommalistsbraced:nw
\mymodule_moveargbehindmergecommalistsbraced:nw{}
\mymodule_mergecommalists:nnwnw
}{#5}
}
}
\cs_new:Npn \mymodule_moveargbehindmergecommalistsunbraced:nw #1#2\mymodule_mergecommalists:nnwnw
{#2 \mymodule_mergecommalists:nnwnw #1}
\cs_new:Npn \mymodule_moveargbehindmergecommalistsbraced:nw #1#2\mymodule_mergecommalists:nnwnw
{#2 \mymodule_mergecommalists:nnwnw {#1}}
\cs_new:Npn \mymodule_mergecommalists:nnwnw #1#2#3,#4#5,
{\tl_if_empty:nTF{#3#5}{\exp_end:{#2}{#4}#1}{\mymodule_mergecommalists:nnwnw {{#2}{#4}#1}#3,#5,}}
%-----------------------------------------------------------------------------------
\mymodule_DefineTestFunction:NnNNn{\TestFunction}{}
{\l__mymodule_keys_clist}
{\l__mymodule_acts_clist}
{The~key~#1~is~not~in~the~list,~thus~default~action!}
\mymodule_DefineTestFunction:NnNNn{\OtherTestFunction}{m}
{\l__mymodule_keys_clist}
{\l__mymodule_acts_clist}
{The~key~#1~is~not~in~the~list,~thus:~#2}
\clist_set:Nn \l__mymodule_keys_clist { key1, key2, key3 }
\clist_set:Nn \l__mymodule_acts_clist { act1, act2, act3 }
\cs_show:c{TestFunction~code}
\cs_show:c{OtherTestFunction~code}
\ExplSyntaxOff
\begin{document}
\TestFunction{key1}
\TestFunction{key2}
\TestFunction{key3}
\TestFunction{key4}
\OtherTestFunction{key1}{This time's default}
\OtherTestFunction{key2}{This time's default}
\OtherTestFunction{key3}{This time's default}
\OtherTestFunction{key4}{This time's default}
\end{document}
如果您希望在执行时指定\TestFunction
要评估的逗号列表变量,您可以这样做:
\documentclass{article}
\ExplSyntaxOn
% \DefineTestFunction{<control word token denoting test function to define>}%
% {<additional xparse-argument-specifiers, denoting #4, #5 etc>
% % #1 is available in <action if #1 is not a key of the keys comma list>
% % in any case and denotes the argument to test.
% % #2 is available in <action if #1 is not a key of the keys comma list>
% % in any case and denotes the <keys comma list>.
% % #3 is available in <action if #1 is not a key of the keys comma list>
% % in any case and denotes the <actions comma list>.
% }%
% {<action if #1 is not a key of the keys comma list>}%
\cs_new:Npn \mymodule_DefineTestFunction:NnNNn #1#2#3 {
\NewDocumentCommand { #1 } { mmm#2 } {
\exp_args:Nno \str_case:nnF {##1}
{%
\exp:w
\mymodule_moveargbehindmergecommalistsunbraced:nw{{},}
\clist_map_function:NN ##3 \mymodule_moveargbehindmergecommalistsbraced:nw
\mymodule_moveargbehindmergecommalistsunbraced:nw{{},}
\clist_map_function:NN ##2 \mymodule_moveargbehindmergecommalistsbraced:nw
\mymodule_moveargbehindmergecommalistsbraced:nw{}
\mymodule_mergecommalists:nnwnw
}{#3}
}
}
\cs_new:Npn \mymodule_moveargbehindmergecommalistsunbraced:nw #1#2\mymodule_mergecommalists:nnwnw
{#2 \mymodule_mergecommalists:nnwnw #1}
\cs_new:Npn \mymodule_moveargbehindmergecommalistsbraced:nw #1#2\mymodule_mergecommalists:nnwnw
{#2 \mymodule_mergecommalists:nnwnw {#1}}
\cs_new:Npn \mymodule_mergecommalists:nnwnw #1#2#3,#4#5,
{\tl_if_empty:nTF{#3#5}{\exp_end:{#2}{#4}#1}{\mymodule_mergecommalists:nnwnw {{#2}{#4}#1}#3,#5,}}
%-----------------------------------------------------------------------------------
\mymodule_DefineTestFunction:NnNNn{\TestFunction}{}
{The~key~#1~is~not~in~the~list,~thus~default~action!}
\mymodule_DefineTestFunction:NnNNn{\OtherTestFunction}{m}
{The~key~#1~is~not~in~the~list,~thus:~#4}
\clist_set:Nn \l__mymodule_keys_clist { key1, key2, key3 }
\clist_set:Nn \l__mymodule_acts_clist { act1, act2, act3 }
\cs_show:c{TestFunction~code}
\cs_show:c{OtherTestFunction~code}
\ExplSyntaxOff
\begin{document}
\ExplSyntaxOn
\TestFunction{key1}{\l__mymodule_keys_clist}{\l__mymodule_acts_clist}\par
\TestFunction{key2}{\l__mymodule_keys_clist}{\l__mymodule_acts_clist}\par
\TestFunction{key3}{\l__mymodule_keys_clist}{\l__mymodule_acts_clist}\par
\TestFunction{key4}{\l__mymodule_keys_clist}{\l__mymodule_acts_clist}\par
\OtherTestFunction{key1}{\l__mymodule_keys_clist}{\l__mymodule_acts_clist}{This~time's~default}\par
\OtherTestFunction{key2}{\l__mymodule_keys_clist}{\l__mymodule_acts_clist}{This~time's~default}\par
\OtherTestFunction{key3}{\l__mymodule_keys_clist}{\l__mymodule_acts_clist}{This~time's~default}\par
\OtherTestFunction{key4}{\l__mymodule_keys_clist}{\l__mymodule_acts_clist}{This~time's~default}
\end{document}