我正在尝试学习 LaTeX3 语法。我想到的一个问题是如何在组内定义具有局部作用域的变量(即 TeX 术语中没有参数的宏)。
在 TeX 中,\def
'ed 宏是定义它的组的本地变量。如果\tl_new:Nn
是 LaTeX3 中的对应项\def\...{...}
,我期望变量\world
也是组的本地变量,但以下代码编译时没有错误:
\documentclass{article}
\usepackage{expl3}
\begin{document}
\ExplSyntaxOn
\group_begin:
\tl_new:Nn\world{world!}
%\def\world{world!}
\group_end:
\ExplSyntaxOff
hello \world
\end{document}
为什么\world
在群外可见?
答案1
\tl_new:XXXX
不像\def
,而更像\newcommand
:它声明了一个新的命令来保存内容。内容被填充,\tl_set
并且存在局部和全局变体:例如。\tl_gset:Nn
和\tl_set:Nn
。
顺便说一句:你的例子对我来说不起作用,我明白
! Undefined control sequence.
l.10 \begin_group
miktex最近expl3
在 2 天前进行了更新。
答案2
为了回答这里隐含的问题(“为什么会这样?”),所有命令\«variable»_new:N
都会创建全局寄存器或宏。因此为了保持一致性, 也这样做\tl_new:Nn
。我们还讨论了添加\«variable»_new_local:N
(请参阅 etex.sty 了解 TeX 中本地寄存器的示例),但决定这样做的好处并不能抵消坏处:
为了:
- 最好有本地寄存器(但不是必需的)
反对:
- 语法和文档变得复杂,因为我们需要解释何时/为什么要使用它们。
我喜欢写作的想法
\group_begin:
\int_new:N \l_track_loop_int
...
« do stuff with \l_track_loop_int »
...
\group_end:
但最终大的问题是:
- 本地宏的行为与本地寄存器不同,这会破坏
tl
数据类型和所有其他数据类型(如int
、dim
等)之间的一致性。(并且我们不会为宏编写分配系统,因为效率会受到影响,而收益却很小。)
举例来说,如果\«var»_new_local:N
分配器存在:
\group_begin:
\tl_new_local:N \foo
\tl_gset:Nn \foo {hello}
\dim_new_local:N \baz
\dim_gset:Nn \baz {1pt}
\group_end:
在这样的构造中,\foo
在组之后仍然可用,而\baz
不能。(请注意,全局设置局部变量是完全有效的。)这是一个足够重要的边缘情况(在我看来),可以最终决定反对这个想法。
因此,推荐的系统是正式声明您打算使用的所有变量(要么在包的开头集中在一起,要么在第一次使用时交叉使用)。但是,\tl_set:Nn
如果需要,在组中使用以创建临时宏没有任何技术问题。
答案3
\tl_new:Nn
扩展为\chk_if_free_cs:N#1\cs_gset_nopar:Npn#1{#2}
且\cs_gset_nopar:Npn
为\gdef
。因此它给出了一个全局定义。