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}