\xx_case: 函数如何工作?

\xx_case: 函数如何工作?

在回答过程中expl3 布尔值开关:是否有 \bool_case:Nnn?,我尝试理解\xx_case:Expl3 提供的各种功能。例如,这里是\tl_case:Npn

\cs_new:Npn \tl_case:Nnn #1#2#3
  {
    \tex_romannumeral:D
    \__tl_case:Nw #1 #2 #1 {#3} \q_recursion_stop
  }
\cs_new:Npn \__tl_case:Nw #1#2#3
  {
    \tl_if_eq:NNTF #1 #2
      { \__tl_case_end:nw {#3} }
      { \__tl_case:Nw #1 }
  }
\cs_new_eq:NN \__tl_case_end:nw \__prg_case_end:nw

从内而外地研究,我发现了这么多:

该命令\__prg_case_end:nw {<code>} <tokens> \q_recursion_stop消耗所有 ⟨代币⟩ 直到\q_recursion_stop,离开 ⟨代码⟩ 在后面。

在内部函数中\__tl_case:Nw,我们测试 ⟨测试标记列表变量⟩ 具体来说 ⟨标记列表变量大小写⟩:如果它们相等,函数终止并返回关联的⟨代码示例⟩;否则函数会产生自身,然后应用于下一{<token-list variable case>} {<code case>}对。

我很难理解如何\tl_case:Nnn设置这个递归。特别是,我知道\tex_romannumeral:D( \romannumeral) 做了一些与扩展相关的有趣的事情,但它在这个上下文中做了什么?

(根据l3quark第 4 和 6 节中记录的递归功能,解释或实现这种函数是否更容易?)

答案1

为了解释发生了什么,我们需要考虑几件事:业务\romannumeral以及我们如何处理未知数量的案例测试。(我也看过这个\romannumeral技巧在我的博客中

\romannumeral语会扩展输入流,直到找到一个整数:这可能是一个文字值,后面跟着一个非数字标记,也可能是存储在寄存器中的整数。“技巧”在于,它不会为负整数或零整数值产生输出。因此,我们需要做的是让它扩展输入,确保我们的“真实”输出永远不会被读为数字,并使用零或负值进行整理。因此,它\romannumeral实际上出现在两个地方:在测试开始时,它确保测试将被扩展,在测试结束时,我们需要终止测试。我们将在下面回到后一点。

案例测试本身通过循环一次运行一个。在示例中,我们有一个标记列表测试,但相同的原理适用于其他case函数。在每个循环中, \__xx_case:都会使用该函数,抓取

  1. 搜索值
  2. 当前测试用例
  3. 如果测试结果为真则使用的代码

测试已执行,如果失败,则进行循环。为了终止循环,设置代码,以便最终测试用例检查搜索值是否与自身相符,该值始终为真,并插入“else”代码。(从技术上讲,最终用例插入#1两次并比较两者,除非#1在使用时是变量,否则结果为真,这种情况只能发生在例如\pdfuniformdeviate 99内部\str_case:nnn:非常不寻常!)

如果测试为真,则\__prg_case_end:nw插入(具有“正确”名称)。它定义为

> \__prg_case_end:nw=\long macro:
#1#2\q_recursion_stop ->\c_zero #1.

因此,它抓取我们想要运行的代码,#1并将其余情况作为#2。然后插入\c_zero 我们想要运行的代码。 是一个被ed 为 的\c_zero标记,因此它就是我们要找的。它也是零,所以不产生输出。因此扩展结束,并且任何设置了 true 情况的代码都可以运行了。(如果没有一个情况为真,代码将是“else”路径的代码。)\chardef0\romannumeral\romannumeral

相关内容