夸克是否必须被定义为向自身扩展的宏?

夸克是否必须被定义为向自身扩展的宏?

当我使用 expl3 的递归函数时,我曾多次遇到夸克被无意扩展的情况,在这种情况下,TeX 将进入无限扩展循环。(更烦人的是,在这种情况下,我的控制台对 Ctrl+C 没有反应,所以我必须手动终止该进程。)

所以我想知道夸克是否必须以这种自膨胀的方式定义。它们似乎有两种用例:

  1. 作为具有分隔参数的各种函数的分隔符,例如\q_recursion_stop。在这种情况下,夸克的定义甚至不重要,因为 TeX 的扩展机制只扫描具有该名称的标记,无论它是否被定义。

  2. 当比较一个 token 是否等于一个 quark 时,例如\quark_if_recursion_tail_stop:N有一个测试

    \if_meaning:w \q_recursion_tail #1 ... \fi:
    

    \if_meaning:w与 相同\ifx,因此它实际上并没有扩展这里的宏,而是对替换文本中的每个标记进行内部比较。这意味着我们在这种情况下也不需要递归宏。类似这样的定义

    \cs_new:Nn \quark {\quark_undefined}
    

    也可以,同时防止意外的无限循环。

有哪些情况绝对有必要采用递归夸克定义?

答案1

我第一个想到的答案是“是的,否则它们就不是夸克了”,因为这是定义,所以这就像问三角形是否必须有三条边一样。但隐含的问题是“夸克在宏定义中有用吗?”。

LaTeX 2e 和 2.09(可能更早)具有与您建议的定义类似的定义。\@nil未定义,通常用在标记列表的末尾,并\@nnil定义为

\def\@nnil{\@nil}

夸克被设计为此的抽象,同时解决了此设置的一些问题。

  • 在阅读代码时,可能会有些困惑,是否应该使用\@nil\@nnil,如果你使用分隔参数,或者使用 遍历列表,则\futurelet需要使用\@nil,但是如果你使用 遍历列表,\def\tmp{#1}\ifx\tmp\@nnil...则需要使用 ,\@nnil因为最后一步将具有 ,\def\tmp{\@nil} 它不\ifx等于\@nil。 Quark 摆脱了这种区别,允许在所有这些上下文中使用相同的名称。

  • 要创建一个新的不同分隔符,您需要另外两个 csnames,(您建议的定义将使所有 quark ifx 相等,您需要在每种情况下都有一个唯一的未定义标记)。在那个时代,latex 严重缺少 csnames,最初许多命令都很脆弱的主要原因是定义一个受保护的 as 版本\fragilefoo会使\def\foo{\protect\fragilefoo} csname 的使用量翻倍,然后 latex 根本无法适应当时的 tex 实现。

这些好处的缺点当然是紧密无限循环导致的有些残酷的错误行为。为了帮助警告不要使用“quark”这个名字,警告需要小心处理它们,可能不应该单独使用......

答案2

这个定义意味着

\tl_set:Nn \l_tmpa_tl { \q_no_value }
\quark_if_no_value:NTF \l_tmpa_tl

逻辑上为真。这使得在处理单个标记时可以快速测试夸克的存在。虽然今天可能不那么重要,但这种方法比其他方法速度更快。夸克是为处理紧密循环中的情况而设置的,这种类型的速度增益可能非常显著。

原因当然是我们正在做

\cs_set_nopar:Npn \q_no_value { \q_no_value }
\cs_set_nopar:Npn \l_tmpa_tl { \q_no_value }
\if_meaning:w \q_no_value \l_tmpa_tl % \tex_ifx:D

这是正确的:两者最终都是\long扩展为的(非)宏\q_no_value

有些部分可以expl3追溯到长的方式,当然这里也有一些“历史”。有了\pdfstrcmp可用的,人们可以基于字符串进行许多测试:这在expl3第一次开发时是不可能的,如果从今天开始,也许人们可能会采取不同的策略。

相关内容