expl3 - 如何将标记列表变量的内容作为参数传递给另一个“函数”?

expl3 - 如何将标记列表变量的内容作为参数传递给另一个“函数”?

我查看了 interface3.pdf 来了解 expl3 1

假设有人使用scratch-token-list-variable \l_tmpa_tl

如何将此变量的内容作为参数传递给另一个“函数” 2

例如,有人希望让\scantokens{..}/处理此变量的内容\tex_scantokens:D或将其传递给<tokens>\tl_rescan:nn {<setup>} {<tokens>}尽管从 interface3.pdf 中难以捉摸的解释中,我没有理解\tex_scantokens:D和之间的区别\tl_rescan:nn——似乎\tl_rescan:nn对行尾字符或换行符或其他什么做了奇怪的事情)。

如何才能做到这一点?

如果一切都失败了:\expandafter您需要多少个\tl_use:N <tl var>才能获得的内容<tl var>


1到目前为止,我学到的主要知识是,当你试图将你读到的个别内容组合成一个可行的东西时,你经常会遇到困难。总是缺少一些东西,然后就开始摆弄,你必须非常了解 TeX,这样你才能使用 TeX 原语自己实现缺少的东西。这反过来又需要一定程度的 TeX 知识,这使得 expl3 本来可以让你免于深入研究的事实变得过时了。

2你为什么打电话expl3 中的“函数”?

答案1

应该绝不\scantokensexpl3代码中使用。

应该绝不在代码中使用\...:D控制序列。expl3

\scantokens/\tex_scantokens:D和之间有什么区别\tl_rescan:nn?实际上,很难决定从哪里开始。

\tl_rescan:nn然而,标准参数,而后面\scantokens应该跟一个<general text>,这已经有很大的差别了。

第一个参数的用途是什么?可以使用它来进行进一步的类别代码设置在重新扫描第二个参数时应用。

但主要的区别在于,\tl_rescan:nn为了修复\scantokens(有几个)的缺点,做了大量工作。

现在说说你问题的重点。expl3语言有非常精确的方式来完成你想要它完成的工作。

如果你有一个函数\marsupialwallaby_foo:n,它接受一个标准的括号参数,你可以通过以下方式将 tl 变量的内容传递给它:

\cs_generate_variant:Nn \marsupialwallaby_foo:n { V }

并调用函数如下

\marsupialwallaby_foo:V \l_tmpa_tl

如果你之前说过,\tl_set:Nn \l_tmpa_tl { baz }之前的调用相当于

\marsupialwallaby_foo:n { baz }

当然,当前的将使用变量的内容。

在你的情况下

\cs_generate_variant:Nn \tl_rescan:nn { nV }

可以让你做到

\tl_rescan:nV { } \l_tmpa_tl

请参阅手册第五部分第 1 至 3 节interface3

顺便说一下,同样的结果也可以通过以下方式获得

\exp_args:NnV \tl_rescan:nn { } \l_tmpa_tl

但生成变体更加方便且更受推荐。

如果你想重新扫描内容\l_tmpa_tl并将结果保存在同一个变量中,你可以这样做

\cs_generate_variant:Nn \tl_set_rescan:Nnn { nnV }

\tl_set_rescan:NnV \l_tmpa_tl { } \l_tmpa_tl

不言而喻,该\cs_generate_variant:Nn操作只需要在需要变体的代码开始时执行一次。


为什么我们使用“函数”这个术语?因为“宏”太通用了,并不能真正反映expl3具有“函数”和“变量”的编程范式。无论它们是作为宏、原语还是寄存器实现,对程序员来说都不重要。

答案2

如果你仔细阅读 interface3.pdf,你会偶然发现参数类型V

interface3.pdf,第 5 章,“5.3 介绍变体”部分解释V-type:

5.3 变体介绍
V类型返回寄存器的值,可以是以下之一:、clist、int、skip、dim、muskip 或内置 TeX 寄存器。类型v相同,只是它首先从其参数中创建一个控制序列,然后再返回值。
一般来说,程序员不需要关心扩展控制。当简单地使用变量的内容时,应该V使用带有说明符的函数。
对于那些由 (cs)name 引用的函数,v说明符可用于相同目的。只有当需要特定的扩展步骤时,例如使用分隔参数时,才应该使用带有 o 说明符的低级函数。

expl3.pdf“4 扩展控制”部分说:

变量的值。
这意味着相关寄存器的内容被用作参数,无论是整数、长度类型寄存器,标记列表变量或类似。该值作为带括号的标记列表传递给函数。可以应用于具有\<var>_use:N函数(浮点和框除外)的变量,因此这些变量传递单个“值”。

对你的问题的一个可能的答案是:

\tl_rescan:nV让 expl3 生成一个变体\tl_rescan:nn并使用该变体:

\cs_generate_variant:Nn \tl_rescan:nn {nV}
...
\tl_rescan:nV {<setup>} {\l_tmpa_tl}

\tl_rescan:nn关于和\scantokens/之间的区别\tex_scantokens:D

在 Knuth 的消化过程类比中,TeX 具有

  • 眼睛,
  • 具有口、食道、胃和肠的消化道。
  • 产生代币并通过消化器官处理代币的能力。

TeX 的眼睛逐行读取 .tex 输入文件,并将一行中的字符放入嘴中。(眼睛会预处理来自一行的字符序列,这一点很重要。但这对于理解\tl_rescan:nn\scantokens/之间的关键区别并不重要\tex_scantokens:D。)嘴将字符视为一组指令,用于生成标记并将其发送到食道。发送到食道的标记形成“标记流”,其元素在 TeX 的食道和/或 TeX 消化道的后续站中进行处理。标记可以是不同风格的控制序列标记或不同风格的字符标记。

TeX 的嘴巴根据字符类别代码提供的规则、诸如的参数值\endlinechar以及“硬编码”到 TeX 程序(或 LaTeX 程序,如果您使用自动加载形成 LaTeX 格式的宏集的变体)中的内容来产生标记。

一个(可扩展的)标记的扩展(即用其他标记替换该标记(以及可能构成其参数的标记))发生在标记通过 TeX 的管道传输时。(除非扩展被抑制,即管道被“告知”不要扩展标记。)TeX 的管道是“扩展站”。

类比中的赋值(定义宏、为寄存器赋值\count等)发生在 TeX 的胃中。生成一系列页面也是由 TeX 的胃完成的。

这个过程将每一页转换成输出文件 (.dvi 文件 / .pdf 文件) 所需的形式。

TeX 消化过程的最终结果由输出文件(.pdf 文件/.dvi 文件、.log 文件、辅助文本文件(例如 .aux 文件和 .toc 文件以及 .lot/.lof 文件等))以及写入控制台的内容形成。

正常情况下,消化站按需工作,并且始终只提供所需量:

当嘴巴需要并因此请求字符时,它会从眼睛那里请求这些字符,而眼睛会提供一行 .tex 输入的字符。当食道需要并因此请求可能扩展的标记时,它会从嘴巴那里请求这些标记,而嘴巴会提供一些标记。
当胃需要标记时,它会从食道那里请求这些标记,而食道会提供一些标记,如果扩展不受抑制,则食道会提供一些标记,从而发挥扩展的巨大作用。

\scantokens/\tex_scantokens:D工作原理如下:

它假装将构成其参数的标记不扩展地写入外部文本文件,然后使 TeX 将目光集中在该“假文本文件”上,将其作为 .tex 输入的来源,而不是实际的 .tex 输入文件。我将其称为“假文本文件”,因为数据不是存储在用于固定数据存储的设备上,而是保存在计算机的 RAM 中。

TeX 的消化过程正常进行,只是眼睛的焦点不是作为 .tex 输入源的实际 .tex 输入文件,而是集中在通过\scantokens'/\tex_scantokens:D假装将其参数未扩展写入保存假文本文件数据的 RAM 中所提供的内容。

与任何其他以\scantokens/\tex_scantokens:D的假文本文件作为 .tex-input 源的 .tex-input 源一样,来自 .tex-input 行的字符仅在需要时才放入嘴中,嘴仅按需为食道产生标记,食道仅按需将标记递送到胃中。

因此,以\scantokens/\tex_scantokens:D作为 .tex 输入源,在 TeX 口中按需放置字符的时间间隔之后,紧接着是 TeX 口中字符被消化的时间间隔,这会导致产生标记并执行/实现这些标记所表示的事物。
例如,这可能会导致执行指令,以改变今后如何从放入 TeX 口中的字符中产生标记。“改变今后如何产生标记的指令可能是:更改类别代码、更改参数值(如 )\endlinechar等。命令(如 )\verb和环境(如 )会触发临时更改类别代码。这些更改还会影响从'/参数verbatim生成的假文本文件的后续内容如何被标记化。\scantokens\tex_scantokens:D

如果我理解正确的\tl_rescan:nn话,\scantokens/\tex_scantokens:D会触发创建一个假文本文件,同时也会触发 TeX 的眼睛聚焦于该假文本文件。

\scantokens/不同,在 /\tex_scantokens:D中,标记仅按需从伪文本文件的字符中生成,而 /\tl_rescan:nn则不会将 .tex 输入字符放入 TeX 口中的时间间隔与消化这些字符的时间间隔 / 与创建、扩展和进一步处理标记的时间间隔混合在一起。
/\tl_rescan:nn标记是一次性从来自伪文本文件的所有字符/行生成的。然后,所有这些标记都会附加到管道的标记流中,并准备进一步消化。

其结果是:

\tl_rescan:nn的参数中用于(暂时)更改类别代码的指令⟨tokens⟩(例如,\verbverbatim-环境带来这样的指令)不会影响的\tl_rescan:nn参数的后续内容如何⟨tokens⟩重新标记,因为当形成指令的标记到达 TeX 的胃中并执行时,这些后续内容已经被重新标记。


对于这个问题:“你为什么在 expl3 中将宏称为“函数”?”

术语“函数”不仅仅是“宏”的解释术语同义词。

严格地说,宏标记是一个消失的标记,并被组成它的标记替换。替换文本⟩ (用参数替换 ⟨替换文本触发一步扩展后,⟩的参数#1, , ...) 。#2

“函数”是在处理/扩展的某个阶段确定会产生结果(通常以一组标记的形式)的东西。

例如,“函数”可以是某些算法在 TeX 中的复杂实现,而该实现也可能由多个(内部)宏的定义组成。
执行函数(可能是扩展驱动的尾递归循环等)可能意味着触发某些扩展步骤,因此结果不一定在恰好一个扩展步骤后交付,而是交付结果可能需要触发多个扩展步骤。


阅读 TeXBook 或 interface3.pdf 等计算机手册时我学到的主要内容是:

您需要像律师一样阅读它们,在审查提交给他审查的合同时权衡每个音节的含义,以查找可能存在的陷阱。;-)

然而,与律师不同的是,您不必为了检测可能存在的恶意而这样做,但您必须这样做以免错过任何重要内容/以免忽视措辞的重要含义。;-)

相关内容