我仍然很难理解 f 型展开。这到底是什么?第 2 页的解释interface3.pdf
确实不太令人满意。
在给定的例子中
\tl_set:Nn \l_mya_tl { A }
\tl_set:Nn \l_myb_tl { B }
\tl_set:Nf \l_mya_tl { \l_mya_tl \l_myb_tl }
,如何检查的内容\l_mya_tl
实际上是A\l_myb_tl
?
\l_mya_tl
为了在第三行进行设置而重新使用,而不是另一个迄今为止未使用的标记列表变量,这是否重要\l_myc_tl
?
\l_mya_tl
既然可以扩张,为何扩张完就停止呢?
是否存在可以想象的场景,即在扩展第一个标记(\l_mya_tl
此处为 )后,f 扩展会继续?如何才能\l_mya_tl
不中断进一步的扩展?
当预期参数真正完全展开时,为什么有人会想要使用在某个不可预测的地方停止的 f 展开?(这就是f
“完全”对我的意义。)
答案1
f
一旦找到不可扩展的标记,类型扩展就会结束;如果该标记是空格(字符代码 32,类别代码 10),它将被吞噬。
您\tl_set:Nf \l_mya_tl { \l_mya_tl\l_myb_tl }
将首先对 进行递归扩展\l_mya_tl
,从而得到A
。这是不可扩展的,因此业务在此停止。要分配的令牌列表被评估为A\l_myb_tl
并\l_mya_tl
更新为包含此列表。
改变的内容\l_myb_tl
也会改变的扩展\l_mya_tl
,因为这个包含一个指针到\l_myb_tl
,而不是这个变量在定义时所具有的值。
如果你想冻结更新后的\l_mya_tl
变量的值的值为\l_mya_tl
,并且\l_myb_tl
您必须使用x
-type 或e
-type 扩展。
最后两种类型导致相同的结果,但有很大区别:e
-type 扩展可以出现在扩展上下文中,而x
-type 不能。在这种情况下区别不大,因为你正在执行赋值。实际上,没有预定\tl_set:Ne
义函数,因为事实证明\tl_set:Ne
需要的时间是 的两倍\tl_set:Nx
。
\documentclass{article}
\usepackage{expl3,l3benchmark}
\ExplSyntaxOn
\tl_set:Nn \l_tmpa_tl { A }
\tl_set:Nn \l_tmpb_tl { B }
\tl_new:N \l_tmpc_tl
\cs_generate_variant:Nn \tl_set:Nn { Ne }
\benchmark:n { \tl_set:Nx \l_tmpc_tl { \l_tmpa_tl \l_tmpb_tl } }
\benchmark:n { \tl_set:Ne \l_tmpc_tl { \l_tmpa_tl \l_tmpb_tl } }
\stop
在我的机器上,
3.16e-7 seconds (1.01 ops)
7.78e-7 seconds (2.39 ops)
无论哪种情况,\l_tmpc_tl
都被分配AB
。
为什么有人想要f
-expansion?好问题!直到几个月前,还没有办法在扩展上下文中进行完全递归扩展。当该原语\expanded
被添加到所有引擎时,情况发生了变化(以前只允许在 LuaTeX 中使用),当然,除了 Knuth TeX。
答案2
与 x 扩展进行比较:
\documentclass{article}
\usepackage{expl3}
\begin{document}
\ExplSyntaxOn
\tl_set:Nn \l_mya_tl { A }
\tl_set:Nn \l_myb_tl { B }
\tl_set:Nf \l_myc_tl { \l_mya_tl STOP \l_myb_tl }
\tl_show:N \l_myc_tl
\tl_set:Nx \l_myc_tl { \l_mya_tl STOP \l_myb_tl }
\tl_show:N \l_myc_tl
\ExplSyntaxOff\end{document}
这将使
> \l_myc_tl=ASTOP\l_myb_tl .
<recently read> }
l.207 \tl_show:N \l_myc_tl
?
> \l_myc_tl=ASTOPB.
<recently read> }
l.210 \tl_show:N \l_myc_tl
答案3
正如在其他答案和评论中指出的那样,f
-expansion 是使用来实现的,在原语可用\romannumeral
之前,有时在扩展上下文中需要它。\expanded
这个答案还提到了两个可能仍然有用的用例,即没有已知终点的扩展和下一个不可扩展标记的前瞻。
此外,我想指出一个常见的用例,它甚至错误的使用它,因为它会产生不理想的结果。这是基于这样的事实:虽然x
-expansion 继续完全扩展第一个不可扩展的标记之外的标记,但-expansion在标记流中使用f
时更为急切。\exp_not:n
如果我们看以下例子,我们会看到当使用\exp:not:N
( ) 时扩展是相同的:\noexpand
\cs_set:Npn \foo { [FOO] }
\tl_set:Nx \l_tmpb_tl { \exp_not:N \foo bar }
\tl_show:N \l_tmpb_tl
\tl_set:Nf \l_tmpb_tl { \exp_not:N \foo bar }
\tl_show:N \l_tmpb_tl
输出
> \l_tmpb_tl=\foo bar.
> \l_tmpb_tl=\foo bar.
另一方面,使用\exp_not:n
( \unexpanded
) 会产生不同的结果:
\tl_set:Nx \l_tmpb_tl { \exp_not:n { \foo } bar }
\tl_show:N \l_tmpb_tl
\tl_set:Nf \l_tmpb_tl { \exp_not:n { \foo } bar }
\tl_show:N \l_tmpb_tl
输出
> \l_tmpb_tl=\foo bar.
> \l_tmpb_tl=[FOO]bar.
\tl_head:
当通过、\tl_tail:
等函数处理标记列表变量的部分内容时,这一点尤其重要\tl_range:
。所有这些函数都将其结果包装在\exp_not:n
.f
扩展中,在这里似乎很合适,但实际上并非如此:
\tl_set:Nn \l_tmpa_tl { \foo bar }
\tl_set:Nx \l_tmpb_tl { \tl_head:V \l_tmpa_tl }
\tl_show:N \l_tmpb_tl
\tl_set:Nf \l_tmpb_tl { \tl_head:V \l_tmpa_tl }
\tl_show:N \l_tmpb_tl
输出
> \l_tmpb_tl=\foo .
> \l_tmpb_tl=[FOO].
正如 Phelype Oleinik 指出的那样,受保护的宏的行为也不同:
\cs_new_protected:Npn \protected_foo { \foo }
\tl_set:Nx \l_tmpb_tl { \protected_foo bar }
\tl_show:N \l_tmpb_tl
\tl_set:Nf \l_tmpb_tl { \protected_foo bar }
\tl_show:N \l_tmpb_tl
输出
> \l_tmpb_tl=\protected_foo bar.
> \l_tmpb_tl=[FOO]bar.