expl3 - 如果函数的顶层宏不处理参数但调用内部宏来处理参数,则顶层宏的参数规范规则

expl3 - 如果函数的顶层宏不处理参数但调用内部宏来处理参数,则顶层宏的参数规范规则

文件 expl3.pdf - “expl3 包和 LaTeX3 编程”解释了应如何命名 expl3 函数,根据方案 还是根据方案。
\⟨module⟩_⟨description⟩:⟨arg-spec⟩

\__⟨module⟩_⟨description⟩:⟨arg-spec⟩

这引出了两个问题:

  1. 如何准确区分“函数”和“宏”?

  2. 假设它是关于程序员的接口,其中接口的程序员/用户要调用一个顶级宏,它本身不处理任何参数,但它反过来会启动扩展级联,在此过程中调用内部宏来处理参数。

    在这种情况下,应该如何处理参数规范的问题?

    我看到两种可能性:

    • 顶层宏的参数规范为空,即顶层宏的名称以冒号结尾:。每个内部宏的名称都带有一个参数规范,该规范准确指定了相应内部宏处理的参数。

    • 顶级宏的参数规范不为空,但指定了在扩展级联过程中,基于宏的机制整体处理哪些类型的参数。每个内部宏的名称都带有一个参数规范,该规范精确指定了相应内部宏处理的参数。
      这种可能性引发了以下额外问题:

      • 是否适用\cs_generate_variant:Nn于顶层宏,以及
      • 例如,如何处理处理可变数量参数的尾递归宏机制的顶级宏的参数规范。

答案1

  1. 如何准确区分“函数”和“宏”?

呃...:)

实际上没有明显的区别。在记录用户级宏时,我倾向于写“函数”,而在编写代码文档时,我更经常使用“宏”。术语“函数”expl3用于区分宏,即来自宏的东西(函数)抓住-stuff(tl例如变量),并且“宏”使用较少,因此原则上应该遵循这一点。

  1. 假设它是关于程序员的接口,其中接口的程序员/用户要调用一个顶级宏,它本身不处理任何参数,但它反过来会启动扩展级联,在此过程中调用内部宏来处理参数。

从你提到的两个选项来看,后者。即使一个函数没有直接地(如:在第一次扩展之后)采用一个参数,其名称必须包含它将在后续步骤中抓取的参数。这正是\cs_generate_variant:Nn在这些情况下起作用的原因。从内核中举一个简单的例子:

\cs_new_protected:Npn \clist_concat:NNN
  { \__clist_concat:NNNN \__kernel_tl_set:Nx }

\clist_concat:NNN不接受任何参数,但这是:NNN因为某些辅助函数将接受这三个参数。这很重要,因为:

  1. (最重要的是)你不必深入挖掘它的内部结构\clist_concat:NNN来弄清楚它需要多少个参数,因为从它的名字就可以看出来;
  2. \cs_generate_variant:Nn \clist_concat:NNN { ccc }将会起作用,因为\cs_generate_variant:Nn知道函数需要多少个参数(并且如果您尝试从中创建变体则会引发错误:ccccc(那么生成的变体将很简单\exp_args:Nccc \clist_concat:NNN)。
  • 例如,如何处理处理可变数量参数的尾递归宏机制的顶级宏的参数规范。

:w。如果你编写一个类似这样的递归函数:

\cs_new:Npn \my_tmp:w #1
  {
    \if_meaning:w \scan_stop: #1
    \else:
      \my_do_stuff_with:n {#1}
      \exp_after:wN \my_tmp:w
    \fi:
  }

然后使用

\my_tmp:w { some } { arguments } { then } { end } { with } { \relax }

您不知道函数将接受多少个参数,因为这取决于输入。这意味着您无法执行\cs_generate_variant:Nn,因为您无法知道要处理多少个参数。也许,如果此类文档\my_tmp:w说它至少需要一个实际参数,那么您可以调用它\my_tmp:nw,并仅为第一个参数生成一个变体。

答案2

让我用一些简单的语言重新表述一下 Phelype 对第 2 点的回答:

expl3 函数的签名(即名称中冒号后面的内容)应该表示全部参数和这些参数的预处理,换句话说,就是该函数在其之后需要的所有东西。这些参数是直接还是间接获取的(出于效率原因或……)完全无关紧要。

例如,如果您写下\foo:nnn,那么阅读您的代码的每个人都会立即知道“foo”后面需要 3 个括号参数。

这里的重点是:“查看签名,然后您就知道该函数将拾取什么作为参数(以及如果签名对参数进行一些预处理,例如使用“c”或“V”或...)


这也解释了我们使用“函数”而不是“宏”的原因(尽管 TeX 是一种宏扩展语言,但你无法完全掩盖这一事实(除了惯例))。函数是你通过签名知道它们接受什么参数的东西(除了在少数情况下它们是“奇怪的”并且具有签名:w),而宏就是宏:对你施展各种花招或你施展各种花招的肮脏东西,我们试图避免在 expl3 编程中这样做(或至少限制在非常内部的东西上)。

相关内容