使用两个逗号列表构建一个使用 \str_case:nn 的测试函数

使用两个逗号列表构建一个使用 \str_case:nn 的测试函数

假设给定两个长度相同的逗号列表:

\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:key1act1\k:key2act2\TestFunctionNo 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:NNUpdated: 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,aITEMS,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}

在此处输入图片描述

相关内容