expl3 - \keyval_parse:NNn - 为什么只有分别吸收 1 个和 2 个参数的函数?

expl3 - \keyval_parse:NNn - 为什么只有分别吸收 1 个和 2 个参数的函数?

interface3.pdf 说:

\keyval_parse:NNn ⟨function1⟩ ⟨function2⟩ {⟨key–value list⟩}
解析⟨键值列表⟩变成一系列⟨键⟩以及相关⟨价值观⟩或单独按键(如果没有 ⟨价值⟩被给予)。 ⟨功能1⟩应该采取一个论点,而 ⟨功能2⟩应该吸收两个参数。 \keyval_parse:NNn解析完⟨键值列表⟩⟨功能1⟩用于处理没有值的键,⟨功能2⟩用于处理给定值的键。 ⟨键⟩在里面⟨键值列表⟩被保存下来
。......

我的问题是:

为什么建议您使用仅吸收 1 个相应的 2 个参数的函数?

我问这个问题是因为这可以防止你放入\keyval_parse:NNn宏定义中

  • ⟨功能1⟩处理几个参数,其中最后一个是⟨钥匙⟩而其他参数来自宏定义的参数/参数。
  • ⟨功能2⟩处理几个参数,其中最后一个是⟨钥匙⟩最后是⟨价值⟩而其他参数来自宏定义的参数/参数。

我本来想做这样的事:

\cs_new:Nn \MyStuff_ProcessInCaseOnlyKey:nnn {
  This~is~the~first~argument~of~\texttt{\token_to_str:N\MyStuff_ProcessAsKeyval:nnn}:~\exp_args:No\texttt{\tl_to_str:n{#1}}.\\
  This~is~the~second~argument~of~\texttt{\token_to_str:N\MyStuff_ProcessAsKeyval:nnn}:~\exp_args:No\texttt{\tl_to_str:n{#2}}.\\
  This~is~the~key~which~was~passed~on~by~the~keyval~parser:~\exp_args:No\texttt{\tl_to_str:n{#3}}.\\
}
\cs_new:Nn \MyStuff_ProcessInCaseKeyAndValue:nnnn {
  This~is~the~first~argument~of~\texttt{\token_to_str:N\MyStuff_ProcessAsKeyval:nnn}:~\exp_args:No\texttt{\tl_to_str:n{#1}}.\\
  This~is~the~second~argument~of~\texttt{\token_to_str:N\MyStuff_ProcessAsKeyval:nnn}:~\exp_args:No\texttt{\tl_to_str:n{#2}}.\\
  This~is~the~key~which~was~passed~on~by~the~keyval~parser:~\exp_args:No\texttt{\tl_to_str:n{#3}}.\\
  This~is~the~value~which~was~passed~on~by~the~keyval~parser:~\exp_args:No\texttt{\tl_to_str:n{#4}}.\\
}
\cs_new:Nn \MyStuff_ProcessAsKeyval:nnn {
  \keyval_parse:NNn { \MyStuff_ProcessInCaseOnlyKey:nnn {#1}{#2} } 
                    { \MyStuff_ProcessInCaseKeyAndValue:nnnn {#1}{#2} }
                    {#3}
}

我希望能够在不需要加倍哈希值的情况下使用它,\MyStuff_ProcessAsKeyval:nnn以防其第一个和第二个参数用于提供带有参数的“内联代码”。

例如我希望能够做到

\MyStuff_ProcessAsKeyval:nnn 
  {\cs_set:Nn \MyStuff_tempa:n{This~is~a~temporary~macro's~argument:~#1}}
  {\cs_set:Nn \MyStuff_tempb:n{This~is~another~temporary~macro's~argument:~#1}}
  {keyA=valueA, keyB=valueB}

代替

\MyStuff_ProcessAsKeyval:nnn 
  {\cs_set:Nn \MyStuff_tempa:n{This~is~a~temporary~macro's~argument:~##1}}
  {\cs_set:Nn \MyStuff_tempb:n{This~is~another~temporary~macro's~argument:~##1}}
  {keyA=valueA, keyB=valueB}

(当然不应该是\keyval_parse:NNn但是\keyval_parse:nnn。但是\keyval_parse:nnn不可用并且不能作为变体派生。当然 - 而不是传递#1#2- 我可以让 TeX 在每次执行时定义两个临时宏并在和\MyStuff_ProcessAsKeyval:nnn的定义中使用它们,但这很麻烦并且似乎效率低下。)\MyStuff_ProcessInCaseOnlyKey:n\MyStuff_ProcessInCaseKeyAndValue:nn

我没有提供 MWE,因为我询问的是事情按原样实施的原因。

我并不是在寻求错误追踪方面的帮助。

答案1

\keyval_parse:NNn采用N-type 参数而不是一组标记( -type)的主要原因n是性能;而事实上这根本就没有必要。

快速而粗略的概念证明,\keyval_parse:NNn很容易适应这种用途。我所做的就是搜索NN并替换它,nn并在解析的前几个步骤中将缺少的括号放在参数周围。(好吧,我使用了新名称,而不是keyval新模块名为ulrichkeyval)。

我不确定我是否发现了每个缺少括号的地方,但代码可以工作,所以我想是的......

结果是,使用单个令牌时代码速度大约慢了 5%(以 100 个键和 100 个键=值对以及 1 个空白元素为基准)。

\documentclass[]{article}

\ExplSyntaxOn
\scan_new:N \s__ulrichkeyval_nil
\scan_new:N \s__ulrichkeyval_mark
\scan_new:N \s__ulrichkeyval_stop
\scan_new:N \s__ulrichkeyval_tail
\group_begin:
  \cs_set_protected:Npn \__ulrichkeyval_tmp:NN #1#2
    {
      \cs_new:Npn \ulrichkeyval_parse:nnn ##1 ##2 ##3
        { \__ulrichkeyval_loop_active:nnw {##1} {##2} \s__ulrichkeyval_mark ##3 #1 \s__ulrichkeyval_tail #1 }
      \cs_new:Npn \__ulrichkeyval_loop_active:nnw ##1 ##2 ##3 #1
        {
          \__ulrichkeyval_if_recursion_tail:w ##3
            \__ulrichkeyval_end_loop_active:w \s__ulrichkeyval_tail
          \__ulrichkeyval_loop_other:nnw {##1} {##2} ##3 , \s__ulrichkeyval_tail ,
          \__ulrichkeyval_loop_active:nnw {##1} {##2} \s__ulrichkeyval_mark
        }
      \cs_new:Npn \__ulrichkeyval_split_other:w ##1 = ##2 \s__ulrichkeyval_mark ##3 ##4 \s__ulrichkeyval_stop
        { ##3 ##1 \s__ulrichkeyval_stop \s__ulrichkeyval_mark ##2 }
      \cs_new:Npn \__ulrichkeyval_split_active:w ##1 #2 ##2 \s__ulrichkeyval_mark ##3 ##4 \s__ulrichkeyval_stop
        { ##3 ##1 \s__ulrichkeyval_stop \s__ulrichkeyval_mark ##2 }
      \cs_new:Npn \__ulrichkeyval_loop_other:nnw ##1 ##2 ##3 ,
        {
          \__ulrichkeyval_if_recursion_tail:w ##3
            \__ulrichkeyval_end_loop_other:w \s__ulrichkeyval_tail
          \__ulrichkeyval_split_active:w ##3 \s__ulrichkeyval_nil
            \s__ulrichkeyval_mark \__ulrichkeyval_split_active_auxi:w
            #2 \s__ulrichkeyval_mark \__ulrichkeyval_clean_up_active:w
            \s__ulrichkeyval_stop
          {##1} {##2}
          \__ulrichkeyval_loop_other:nnw {##1} {##2} \s__ulrichkeyval_mark
        }
      \cs_new:Npn \__ulrichkeyval_split_active_auxi:w ##1 \s__ulrichkeyval_stop
        {
          \__ulrichkeyval_split_other:w ##1 \s__ulrichkeyval_nil
            \s__ulrichkeyval_mark \__ulrichkeyval_misplaced_equal_after_active_error:w
            = \s__ulrichkeyval_mark \__ulrichkeyval_split_active_auxii:w
            \s__ulrichkeyval_stop
        }
      \cs_new:Npn \__ulrichkeyval_split_active_auxii:w
          ##1 \s__ulrichkeyval_nil \s__ulrichkeyval_mark \__ulrichkeyval_misplaced_equal_after_active_error:w
          \s__ulrichkeyval_stop \s__ulrichkeyval_mark
        { \__ulrichkeyval_trim:nN { ##1 } \__ulrichkeyval_split_active_auxiii:w }
      \cs_new:Npn \__ulrichkeyval_split_active_auxiii:w ##1 ##2 \s__ulrichkeyval_nil
        {
          \__ulrichkeyval_split_active:w ##2 \s__ulrichkeyval_nil
            \s__ulrichkeyval_mark \__ulrichkeyval_misplaced_equal_in_split_error:w
            #2 \s__ulrichkeyval_mark \__ulrichkeyval_split_active_auxiv:w
            \s__ulrichkeyval_stop
            { ##1 }
        }
      \cs_new:Npn \__ulrichkeyval_split_active_auxiv:w
          ##1 \s__ulrichkeyval_nil \s__ulrichkeyval_mark \__ulrichkeyval_misplaced_equal_in_split_error:w
          \s__ulrichkeyval_stop \s__ulrichkeyval_mark
        {
          \__ulrichkeyval_split_other:w ##1 \s__ulrichkeyval_nil
            \s__ulrichkeyval_mark \__ulrichkeyval_misplaced_equal_in_split_error:w
            = \s__ulrichkeyval_mark \__ulrichkeyval_split_active_auxv:w
            \s__ulrichkeyval_stop
        }
      \cs_new:Npn \__ulrichkeyval_split_active_auxv:w
          ##1 \s__ulrichkeyval_nil \s__ulrichkeyval_mark \__ulrichkeyval_misplaced_equal_in_split_error:w
          \s__ulrichkeyval_stop \s__ulrichkeyval_mark
        { \__ulrichkeyval_trim:nN { ##1 } \__ulrichkeyval_pair:nnnn }
      \cs_new:Npn \__ulrichkeyval_clean_up_active:w
          ##1 \s__ulrichkeyval_nil \s__ulrichkeyval_mark \__ulrichkeyval_split_active_auxi:w \s__ulrichkeyval_stop \s__ulrichkeyval_mark
        {
          \__ulrichkeyval_split_other:w ##1 \s__ulrichkeyval_nil
            \s__ulrichkeyval_mark \__ulrichkeyval_split_other_auxi:w
            = \s__ulrichkeyval_mark \__ulrichkeyval_clean_up_other:w
            \s__ulrichkeyval_stop
        }
      \cs_new:Npn \__ulrichkeyval_split_other_auxi:w ##1 \s__ulrichkeyval_stop
        { \__ulrichkeyval_trim:nN { ##1 } \__ulrichkeyval_split_other_auxii:w }
      \cs_new:Npn \__ulrichkeyval_split_other_auxii:w ##1 ##2 \s__ulrichkeyval_nil
        {
          \__ulrichkeyval_split_other:w ##2 \s__ulrichkeyval_nil
            \s__ulrichkeyval_mark \__ulrichkeyval_misplaced_equal_in_split_error:w
            = \s__ulrichkeyval_mark \__ulrichkeyval_split_other_auxiii:w
            \s__ulrichkeyval_stop
            { ##1 }
        }
      \cs_new:Npn \__ulrichkeyval_split_other_auxiii:w
          ##1 \s__ulrichkeyval_nil \s__ulrichkeyval_mark \__ulrichkeyval_misplaced_equal_in_split_error:w
          \s__ulrichkeyval_stop \s__ulrichkeyval_mark
        { \__ulrichkeyval_trim:nN { ##1 } \__ulrichkeyval_pair:nnnn }
      \cs_new:Npn \__ulrichkeyval_clean_up_other:w
          ##1 \s__ulrichkeyval_nil \s__ulrichkeyval_mark \__ulrichkeyval_split_other_auxi:w \s__ulrichkeyval_stop \s__ulrichkeyval_mark
        {
          \__ulrichkeyval_if_blank:w ##1 \s__ulrichkeyval_nil \s__ulrichkeyval_stop \__ulrichkeyval_blank_true:w
            \s__ulrichkeyval_mark \s__ulrichkeyval_stop \use:n
            { \__ulrichkeyval_trim:nN { ##1 } \__ulrichkeyval_key:nnn }
        }
      \cs_new:Npn \__ulrichkeyval_misplaced_equal_after_active_error:w
          \s__ulrichkeyval_mark ##1 \s__ulrichkeyval_stop \s__ulrichkeyval_mark ##2 \s__ulrichkeyval_nil
          \s__ulrichkeyval_mark ##3 \s__ulrichkeyval_nil ##4 ##5
        {
          \__kernel_msg_expandable_error:nn
            { kernel } { misplaced-equals-sign }
        }
      \cs_new:Npn \__ulrichkeyval_misplaced_equal_in_split_error:w
          \s__ulrichkeyval_mark ##1 \s__ulrichkeyval_stop \s__ulrichkeyval_mark ##2 \s__ulrichkeyval_nil
          ##3 ##4 ##5
        {
          \__kernel_msg_expandable_error:nn
            { kernel } { misplaced-equals-sign }
        }
      \cs_new:Npn \__ulrichkeyval_end_loop_other:w
          \s__ulrichkeyval_tail
          \__ulrichkeyval_split_active:w ##1 \s__ulrichkeyval_nil
          \s__ulrichkeyval_mark \__ulrichkeyval_split_active_auxi:w
          #2 \s__ulrichkeyval_mark \__ulrichkeyval_clean_up_active:w
          \s__ulrichkeyval_stop
          ##2 ##3
          \__ulrichkeyval_loop_other:nnw ##4 \s__ulrichkeyval_mark
        { }
      \cs_new:Npn \__ulrichkeyval_end_loop_active:w
          \s__ulrichkeyval_tail
          \__ulrichkeyval_loop_other:nnw ##1 , \s__ulrichkeyval_tail ,
          \__ulrichkeyval_loop_active:nnw ##2 \s__ulrichkeyval_mark
        { }
    }
  \char_set_catcode_active:n { `\, }
  \char_set_catcode_active:n { `\= }
  \__ulrichkeyval_tmp:NN , =
\group_end:
\cs_new:Npn \__ulrichkeyval_pair:nnnn #1 #2 #3 #4
  {
    \__ulrichkeyval_if_blank:w \s__ulrichkeyval_mark #2 \s__ulrichkeyval_nil \s__ulrichkeyval_stop \__ulrichkeyval_blank_key_error:w
      \s__ulrichkeyval_mark \s__ulrichkeyval_stop
    \exp_not:n { #4 { #2 } { #1 } }
  }
\cs_new:Npn \__ulrichkeyval_key:nnn #1 #2 #3
  {
    \__ulrichkeyval_if_blank:w \s__ulrichkeyval_mark #1 \s__ulrichkeyval_nil \s__ulrichkeyval_stop \__ulrichkeyval_blank_key_error:w
      \s__ulrichkeyval_mark \s__ulrichkeyval_stop
    \exp_not:n { #2 { #1 } }
  }
\cs_new:Npn \__ulrichkeyval_if_empty:w #1 \s__ulrichkeyval_mark \s__ulrichkeyval_stop { }
\cs_new:Npn \__ulrichkeyval_if_blank:w \s__ulrichkeyval_mark #1 { \__ulrichkeyval_if_empty:w \s__ulrichkeyval_mark }
\cs_new:Npn \__ulrichkeyval_if_recursion_tail:w \s__ulrichkeyval_mark #1 \s__ulrichkeyval_tail { }
\cs_new:Npn \__ulrichkeyval_blank_true:w \s__ulrichkeyval_mark \s__ulrichkeyval_stop \use:n #1 #2 #3 { }
\cs_new:Npn \__ulrichkeyval_blank_key_error:w \s__ulrichkeyval_mark \s__ulrichkeyval_stop \exp_not:n #1
  {
    \__kernel_msg_expandable_error:nn
      { kernel } { blank-key-name }
  }
\group_begin:
  \cs_set_protected:Npn \__ulrichkeyval_tmp:n #1
    {
      \cs_new:Npn \__ulrichkeyval_trim:nN ##1
        {
          \__ulrichkeyval_trim_auxi:w
            ##1
            \s__ulrichkeyval_nil
            \s__ulrichkeyval_mark #1 { }
            \s__ulrichkeyval_mark \__ulrichkeyval_trim_auxii:w
            \__ulrichkeyval_trim_auxiii:w
            #1 \s__ulrichkeyval_nil
            \__ulrichkeyval_trim_auxiv:w
          \s__ulrichkeyval_stop
        }
      \cs_new:Npn \__ulrichkeyval_trim_auxi:w ##1 \s__ulrichkeyval_mark #1 ##2 \s__ulrichkeyval_mark ##3
        {
          ##3
          \__ulrichkeyval_trim_auxi:w
          \s__ulrichkeyval_mark
          ##2
          \s__ulrichkeyval_mark #1 {##1}
        }
      \cs_new:Npn \__ulrichkeyval_trim_auxii:w \__ulrichkeyval_trim_auxi:w \s__ulrichkeyval_mark \s__ulrichkeyval_mark ##1
        {
          \__ulrichkeyval_trim_auxiii:w
          ##1
        }
      \cs_new:Npn \__ulrichkeyval_trim_auxiii:w ##1 #1 \s__ulrichkeyval_nil ##2
        {
          ##2
          ##1 \s__ulrichkeyval_nil
          \__ulrichkeyval_trim_auxiii:w
        }
      \cs_new:Npn \__ulrichkeyval_trim_auxiv:w \s__ulrichkeyval_mark ##1 \s__ulrichkeyval_nil ##2 \s__ulrichkeyval_stop ##3
        { ##3 { ##1 } }
    }
  \__ulrichkeyval_tmp:n { ~ }
\group_end:

\cs_new:Nn \MyStuff_ProcessInCaseOnlyKey:nnn {
  This~is~the~first~argument~of~\texttt{\token_to_str:N\MyStuff_ProcessAsKeyval:nnn}:~#1.\\
  This~is~the~second~argument~of~\texttt{\token_to_str:N\MyStuff_ProcessAsKeyval:nnn}:~#2.\\
  This~is~the~key~which~was~passed~on~by~the~keyval~parser:~#3.\par
}
\cs_new:Nn \MyStuff_ProcessInCaseKeyAndValue:nnnn {
  This~is~the~first~argument~of~\texttt{\token_to_str:N\MyStuff_ProcessAsKeyval:nnn}:~#1.\\
  This~is~the~second~argument~of~\texttt{\token_to_str:N\MyStuff_ProcessAsKeyval:nnn}:~#2.\\
  This~is~the~key~which~was~passed~on~by~the~keyval~parser:~#3.\\
  This~is~the~value~which~was~passed~on~by~the~keyval~parser:~#4.\par
}
\cs_new:Nn \MyStuff_ProcessAsKeyval:nnn {
  \ulrichkeyval_parse:nnn
    { \MyStuff_ProcessInCaseOnlyKey:nnn {#1}{#2} } 
    { \MyStuff_ProcessInCaseKeyAndValue:nnnn {#1}{#2} }
    {#3}
}
\cs_new_eq:NN \MyStuff \MyStuff_ProcessAsKeyval:nnn
\ExplSyntaxOff

\begin{document}
\MyStuff{A}{B}{key,key=val,val,val=key}
\end{document}

答案2

你应该有全部表达的论点:

\MyStuff_ProcessInCaseOnlyKey:nnn {#1}{#2}{##1}

\MyStuff_ProcessInCaseKeyAndValue:nnnn {#1}{#2}{##1}{##2}

我是说真的:没有规定为什么键和值要作为最后一个参数传递。代码也更清晰。

\documentclass{article}

\ExplSyntaxOn

\cs_new_protected:Nn \ulrich_keyval_parse:nnn
 {
  \cs_set:Nn \__ulrich_keyval_parse_single:n { #1 }
  \cs_set:Nn \__ulrich_keyval_parse_single:nn { #2 }
  \keyval_parse:NNn \__ulrich_keyval_parse_single:n \__ulrich_keyval_parse_single:nn { #3 }
 }

\ulrich_keyval_parse:nnn
 { \iow_term:n { key~is~#1 } }
 { \iow_term:n { key~is~#1;~value~is~#2 } }
 {key,key=val,val,val=key}

\begin{document}

\cs_new_protected:Nn \MyStuff_ProcessInCaseOnlyKey:nnn
 {
  This~is~the~first~argument~of~\texttt{\token_to_str:N\MyStuff_ProcessAsKeyval:nnn}:~#1.\\
  This~is~the~second~argument~of~\texttt{\token_to_str:N\MyStuff_ProcessAsKeyval:nnn}:~#2.\\
  This~is~the~key~which~was~passed~on~by~the~keyval~parser:~#3.
 }
\cs_new_protected:Nn \MyStuff_ProcessInCaseKeyAndValue:nnnn
 {
  This~is~the~first~argument~of~\texttt{\token_to_str:N\MyStuff_ProcessAsKeyval:nnn}:~#1.\\
  This~is~the~second~argument~of~\texttt{\token_to_str:N\MyStuff_ProcessAsKeyval:nnn}:~#2.\\
  This~is~the~key~which~was~passed~on~by~the~keyval~parser:~#3.\\
  This~is~the~value~which~was~passed~on~by~the~keyval~parser:~#4.
 }
\cs_new:Nn \MyStuff_ProcessAsKeyval:nnn
 {
  \ulrich_keyval_parse:nnn
   { \MyStuff_ProcessInCaseOnlyKey:nnn {#1}{#2}{##1} } 
   { \MyStuff_ProcessInCaseKeyAndValue:nnnn {#1}{#2}{##1}{##2} }
   {#3}
 }

\MyStuff_ProcessAsKeyval:nnn { A } { B } { key,key=val }

\end{document}

相关内容