使用 \tl_map_variable:nNn 进行无限递归

使用 \tl_map_variable:nNn 进行无限递归

我正在尝试使用 LaTeX3 的映射命令之一,\tl_map_variable:nNn如下所示:

\documentclass{article}
\usepackage{expl3}

\begin{document}
\ExplSyntaxOn
\tl_new:N \l_foo_tl
\tl_set:Nn \l_foo_tl {x}

\tl_use:N \l_foo_tl
\tl_map_variable:nNn {abc} \l_foo_tl {\tl_use:N \l_foo_tl}
\tl_use:N \l_foo_tl

\ExplSyntaxOff
\end{document}

预期输出是xabcx。但是,map 函数似乎陷入了无限循环,我不确定为什么会发生这种情况。即使将标记列表更改{abc}{}也会产生相同的结果。

通过启用日志文件中的宏跟踪,我可以发现问题与\q_recursion_tail一遍又一遍地扩展为自身有关。

有人能解释一下这里出了什么问题吗?

答案1

编辑(由BLF):现在已经修复了这个问题:\tl_map_variable:nNn相关函数让变量等于标记列表中的最后一项,而不是尾随标记。

\tl_map_variable:nNn不创建组级别,因此代码末尾\l_foo_tl包含列表中的最后一个标记,即\q_recursion_tail(插入到您的后面abc以检测列表的末尾)。然后,当您执行时,\tl_use:N \l_foo_tl您会展开\q_recursion_tail,从而导致无限递归。

\tl_map_variable:nNn展开一次为:

\__tl_map_variable:Nnn #2 {#3} #1 \q_recursion_tail
\prg_break_point:Nn \tl_map_break: { }

首先要做\__tl_map_variable:Nnn的是将 中的第一个项#1(最终为\q_recursion_tail)分配给#2,即⟨tl var⟩。您应该使用另一个⟨tl var⟩来避免这种情况:

\documentclass{article}
\usepackage{expl3}

\begin{document}
\ExplSyntaxOn
\tl_new:N \l_foo_tl
\tl_new:N \l_bar_tl
\tl_set:Nn \l_foo_tl {x}

\tl_use:N \l_foo_tl
\tl_map_variable:nNn {abc} \l_bar_tl { \tl_use:N \l_bar_tl }
\tl_use:N \l_foo_tl

\ExplSyntaxOff
\end{document}

文档中没有提到(至少我找不到),⟨tl var⟩结束后的内容和可用性\tl_map_variable:nNn,所以我不知道这是否是设计使然。

但是,您可以定义一个版本,在函数结束时\tl_map_variable:nNn保存值并恢复它:⟨tl var⟩

\documentclass{article}
\usepackage{expl3}

\ExplSyntaxOn
\cs_new_protected:Npn \siracusa_tl_map_variable:nNn #1#2
  {
    \exp_args:NV
    \__siracusa_tl_map_variable:nnNn #2 {#1} #2
  }
\cs_new_protected:Npn \__siracusa_tl_map_variable:nnNn #1#2#3#4
  {
    \tl_map_variable:nNn {#2} #3 {#4}
    \tl_set:Nx #3 { \exp_not:n {#1} }
  }
\ExplSyntaxOff

\begin{document}
\ExplSyntaxOn
\tl_new:N \l_foo_tl
\tl_set:Nn \l_foo_tl {x}

\tl_use:N \l_foo_tl
\siracusa_tl_map_variable:nNn {abc} \l_foo_tl { \tl_use:N \l_foo_tl }
\tl_use:N \l_foo_tl

\ExplSyntaxOff
\end{document}

相关内容