在回答过程中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:
都会使用该函数,抓取
- 搜索值
- 当前测试用例
- 如果测试结果为真则使用的代码
测试已执行,如果失败,则进行循环。为了终止循环,设置代码,以便最终测试用例检查搜索值是否与自身相符,该值始终为真,并插入“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”路径的代码。)\chardef
0
\romannumeral
\romannumeral