我目前正在尝试学习expl3
和阅读interface3.pdf
。得益于 提供的参数扩展控制l3expan
,我不必经常定义带有“奇怪”参数的函数。因此,在定义新函数时我通常会有两个选择。
- 我可以使用参数文本定义它,例如
\cs_new:Npn \apply_to:nN #1 #2 { #2 {#1} }
- 我可以使用签名来定义它,例如
\cs_new:Nn \apply_to:nN { #2 {#1} }
我猜想选项 2 通常更可取,因为它可以确保一致性,并且性能通常不会成为问题。但是,似乎所有示例都使用interface3.pdf
选项 1(当然,选项 2 的文档除外)。
所以,我的问题是:这两个选项中应该选择哪一个?在什么情况下这种情况会改变?如果选项 1 是可取的,那为什么要提供选项 2 呢?
答案1
选项一是我通常喜欢的,仅仅是因为它的纯度。
当我们查看的宏跟踪输出时\cs_new:Npn
,我们得到了合理数量的六个宏扩展:
\cs_new:Npn #1->\__kernel_chk_if_free_cs:N #1\cs_gset:Npn #1
#1<-\apply_to:nN
\__kernel_chk_if_free_cs:N #1->\cs_if_free:NF #1{\__kernel_msg_error:nnxx {kern
el}{command-already-defined}{\token_to_str:N #1}{\token_to_meaning:N #1}}
#1<-\apply_to:nN
\cs_if_free:NF #1->\if_meaning:w #1\scan_stop: \prg_return_true: \else: \if_cs_
exist:N #1\prg_return_false: \else: \prg_return_true: \fi: \fi: \exp_end: {}
#1<-\apply_to:nN
\prg_return_true: ->\exp_after:wN \use_i:nn \exp:w
\use_i:nn #1#2->#1
#1<-
#2<-\__kernel_msg_error:nnxx {kernel}{command-already-defined}{\token_to_str:N
\apply_to:nN }{\token_to_meaning:N \apply_to:nN }
\cs_gset:Npn ->\tex_long:D \tex_gdef:D
同样的日志\cs_new:Nn
大概有360行。
我知道人们会反对“但是现在的电脑速度很快!”,因此并不关心这个。对我来说,更高效的版本似乎更简洁,特别是因为我们只需在代码中添加几个字符就可以“免费”获得它。
此外,\cs_new:Nn
不能用作和的组合的简写\cs_new:Npn
。\cs_generate_variant:Nn
尝试定义\cs_set:Nn \apply_to:oc
将失败,并显示
! LaTeX3 Error: Function '\apply_to:oc' is not a base function
(据我所知,这在理论上是可能的,因为每个参数说明符都可以明确地映射到基类型。在这种情况下,:oc
我们可以从中推断出基类型:nN
,定义函数\apply_to:nN
,最后生成\apply_to:oc
从中生成。)
选项二如果我们必须处理函数的动态创建,这仍然非常方便。例如,看看这个答案问题在于将参数文本从标记列表添加到函数定义中,这可以通过使用完全避免\cs_new:Nn
(实际上\cs_set:Nn
,但原理是一样的)。