当我不需要时,我是否应该使用显式参数列表来定义 expl3 函数?

当我不需要时,我是否应该使用显式参数列表来定义 expl3 函数?

我目前正在尝试学习expl3和阅读interface3.pdf。得益于 提供的参数扩展控制l3expan,我不必经常定义带有“奇怪”参数的函数。因此,在定义新函数时我通常会有两个选择。

  1. 我可以使用参数文本定义它,例如
    \cs_new:Npn \apply_to:nN #1 #2 {
        #2 {#1}
    }
    
  2. 我可以使用签名来定义它,例如
    \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,但原理是一样的)。

相关内容