何时使用 \tl_to_str:V,何时使用 \tl_to_str:N?

何时使用 \tl_to_str:V,何时使用 \tl_to_str:N?

根据 interface3 文档,\tl_to_str:N需要一个“tl var”,我相信可能是例如\g_temp_tl

\tl_to_str:V如果我理解正确的话,应该期望一个代表引用变量的控制序列的参数,即一个标记列表变量,我认为它类似于\g_temp_tl

那么使用上有什么区别呢?

请给出一个\tl_to_str:N-variantV错误的例子,并给出另一个\tl_to_str:V-variantN错误的例子。

答案1

\tl_to_str:V是 的变体\tl_to_str:n,即它检索 的值任何变量,并将其传递给\tl_to_str:n\tl_to_str:N另一方面,它需要一个标记列表变量并将其值转换为字符串。重要的是,这是一个独立于 的函数\tl_to_str:n不是一个变体。

只要您只将标记列表变量传递给这些函数,它们的行为就会完全相同,只是\tl_to_str:N比 略快\tl_to_str:V。如果将不同类型的变量传递给两个函数,结果可能会有所不同,具体取决于变量类型。(将除标记列表变量以外的任何变量传递给 都是不正确的\tl_to_str:N!)

\documentclass{article}

\usepackage[T1]{fontenc}
\usepackage{expl3}

\begin{document}

\ExplSyntaxOn

\tl_set:Nn  \l_tmpa_tl  {42}
\int_set:Nn \l_tmpa_int {42}
\fp_set:Nn  \l_tmpa_fp  {42}

\tl_to_str:N \l_tmpa_tl  % 42
\par
\tl_to_str:V \l_tmpa_tl  % 42
\par
\tl_to_str:N \l_tmpa_int % \l_tmpa_int
\par
\tl_to_str:V \l_tmpa_int % 42
\par
\tl_to_str:N \l_tmpa_fp  % \s__fp \__fp_chk:w 10{2}{4200}{0000}{0000}{0000};
\par
\tl_to_str:V \l_tmpa_fp  % \s__fp \__fp_chk:w 10{2}{4200}{0000}{0000}{0000};

\ExplSyntaxOff

\end{document}

请注意价值调用时检索\exp_args:NV并传递给的不一定是人类可读的形式(在注释中称之为“字符串表示”)。我们不妨称其为\tl_to_str:n\tl_to_str:V价值表达。这种表示的定义属性是,它可以被处理相应变量类型的函数理解,即,您可以将其输入\s__fp \__fp_chk:w 10{2}{4200}{0000}{0000}{0000};到任何fp函数中,而不是,42并且其行为相同。(您可以将其视为\exp_args:NV检索变量值的二进制表示,尽管这显然只是一个比喻。)

为人类读者格式化变量的值是一项完全不同的任务。请注意,尽管它可能看起来像\tl_to_str:N(或:V对于标记列表确实如此,但事实并非如此(在某种意义上,您获得了确定变量确切值的所有信息),因为它不显示类别代码。


关于您对示例的要求:\tl_to_str:V \l_tmpa_int是正确的,\tl_to_str:N \l_tmpa_int是错误的。当您可以使用时(即在变量上)\tl_to_str:V永远不会错,但它较慢。\tl_to_str:Ntl

后一种情况下,两个函数之间的真正区别是概念上的:\tl_to_str:N作用于变量\tl_to_str:V作用于其内容。在这种情况下,结果是相同的:[转换 [此标记列表变量] 的内容] 与 [转换 [此标记列表变量的内容]] 相同。对于其他函数,可能会有差异,例如,版本:N通常会为变量分配一个新值,而:V版本会将结果留在输入流中。可扩展性也可能存在差异。

答案2

我看到schtandard 写了一个很好的答案,但我想我可以详细阐述一下n-、N- 和V-type 参数之间的区别。我还会介绍一些历史和技术细节,这可能有助于说明一些想法。

的核心参数类型expl3N(不带括号的单个标记)和n(括号对中的零个或多个标记)。对于标记列表,我们使用这两种类型来分离作用于标记列表的函数多变的来自那些根据“原始”令牌列表行事的人

\tl_map_function:NN \l_tmpa_tl \my_func:n
\tl_map_function:nN { abc } \my_func:n

从历史上看(直到我加入 LaTeX 团队的时候),我们将标记列表变量称为“标记列表指针”(tlp),并且这些功能是分开的:这使得一些想法更清晰一些,但使其他想法变得更加不透明,并且确实总体上令人困惑。

标记列表变量最终是 TeX 宏,因此我们可以使用 在 TeX 原始级别获取它们的“值” \expandafter,或者用以下expl3方式获取\exp_args:No

\exp_args:No \tl_map_function:nN \l_tmpa_tl \my_func:n

因此相当于

\tl_map_function:NN \l_tmpa_tl \my_func:n

问题在于我依赖于 的实现\l_tmpa_tl。我们可以使用 TeX 令牌寄存器 (toks) 来存储数据,并且它们只能通过原语转换为其内容\the。因此应用\exp_args:No是有问题的:理想情况下,使用 时expl3您不需要知道实现细节。(只要 API 保持不变,团队保留更改实现的权利。)

-typeV参数处理了这个问题。它使用 Morten 编写的一些巧妙的代码来“查看”变量并确定它是宏还是 TeX 寄存器。然后它根据需要执行\expandafter\the,只留下“值”。这意味着(如 schtandard 的回答所示)我们可以使用V-type 扩展并将其应用于tl, dim,ETC。而不必担心它们是如何实现的。

(从历史上看,expl3有一个单独的模块,并且它们与/toks并行使用:拥有一种简单的方法来获取值非常重要。我们改变了数据存储以避免需要太多,所以这方面现在表现得不那么强烈。)tlptltltoks

几乎所有\tl_<thing>:N“使用”函数都可以被 替换\tl_<thing>:V,但性能会受到影响。在实现级别,团队“被允许”利用原始 TeX 术语中事物的定义方式。例如,\tl_to_str:N目前确实利用了tl可以在单次扩展中产生值的事实:

> \tl_to_str:N=\long macro:
#1->\__kernel_tl_to_str:w \exp_after:wN {#1}.
l.3 \show\tl_to_str:N

从原始意义上来说

\detokenize\expandafter{#1}

从而尽快完成这一任务。

相关内容