Lua 变量作用域讨论 |
|
VersionNotice: 此页面上的评论指的是一个相当旧版本的 Lua(2001 年左右),该版本具有更有限的词法作用域,并且可能没有完整的闭包支持。这些评论中的许多内容不适用于最近版本的 Lua。
Lua 是静态作用域的 [1]。
关于这种情况是否应该被称为有限/错误的静态/词法作用域存在一些分歧。一些计算机语言教材将词法作用域和静态作用域定义为相同,这使问题变得更加复杂。当访问的变量位于全局作用域时,Lua 的行为与词法作用域一致。
do -- assume "a" is a global with value 5 local foo = function() print(a) end a = 10 foo() -- prints "10" end
当使用 upvalue 访问下一个外层作用域时,Lua 的行为也与词法作用域一致,只要变量没有重新绑定(无论是从变量的作用域还是从嵌套函数)。
do local a = 5 local foo = function() print(%a) end foo() -- prints "5" end
无论如何,Lua 可能不像 Perl 这样的其他脚本语言那样糟糕,尤其是在它们早期的时候。Perl 最初使用动态作用域。它逐渐演变成通过 my
限定符支持词法作用域。词法作用域可能会成为 Perl 6 的默认设置。
Python 以前也有类似于 Lua 的有限静态作用域。词法作用域将成为 2.2 版本的默认设置,并且在 2.1 版本中已经是可选的 [2]。Python 中词法作用域的实现不允许从嵌套函数内部重新绑定变量,这与 Lua 的 upvalue 类似。
对于 Lua,也许只需要将它有限的子集扩展到真正的词法作用域。它可以继续使用 upvalue 概念,并且不允许像 Python 那样重新绑定。或者它可以允许重新绑定,并消除对 upvalue 的需求,因为 upvalue 往往是 Lua 程序员混淆的来源。在 Python 词法作用域的设计文档中,不允许多次绑定的主要原因是 Python 缺乏变量声明。由于 Lua 要求使用 local
来限定变量,因此这将不是问题。
-- E. Toernig
摘自 Aho、Sethi 和 Ullman 于 1986 年出版的“编译器:原理、技术和工具”,第 411 页,Addison-Wesley
将 FOLDOC 条目与龙书中的内容进行比较,龙书的定义似乎有点宽松,因为它没有将“最小块”规定作为必要条件,并且只说它是 C 等某些语言的属性。换句话说,只要给定的作用域规则不是动态的(即不依赖于当前激活),就可以被认为是静态的。
在 Lua 语言中,每个变量要么具有局部作用域,要么具有全局作用域。函数可以在函数内部定义,但是,嵌套函数无法访问其任何封闭函数中定义的变量。因此,Lua 变量缺乏静态嵌套作用域。因此,以下 Lua 代码是非法的
function addn(x) function sum(y) return x+y end return sum end print((addn(3))(1))
此示例是非法的,因为在 addn 中定义的变量 x 无法被嵌套函数 sum 访问。
嵌套函数可以访问其直接封闭函数中定义的变量的副本。对函数内变量的 upvalue 引用会在函数求值时提取此副本以生成闭包。以百分号开头的变量引用表示 upvalue 引用。在 sum 中的 x 引用前面加上百分号,使上面的示例成为合法的 Lua 代码。
这种对静态嵌套作用域的糟糕替代方案之所以被采用,是因为它使实现变得容易,在该实现中,所有非全局变量都分配在堆栈中,但是,不必放弃静态嵌套作用域来拥有分配在堆栈中的非全局变量。
2.2 版之前的 Python 语言具有类似于 Lua 当前规则的作用域规则——每个变量要么具有局部作用域,要么具有全局作用域。从 2.2 版开始,Python 具有静态嵌套作用域。在 Python 的实现中,从嵌套函数引用的非全局变量是不可变的。有了这个额外的假设,分配在堆栈中的非全局变量很容易实现。
Python 的实现证明了这种方法的有效性。由于使用了扁平闭包,它们具有快速实现,如 Luca Cardelli 在 1984 年发表的题为“编译函数式语言”的论文中所述[3]。相关部分是关于“获取变量”的第 4 部分。
对于我们这些希望未来版本的 Lua 具有具有静态嵌套作用域的变量的人来说,我建议我们可以通过寻找核心 Lua 实现者可以使用的实现技术来提供帮助。我认为我们应该仔细研究 Python 2.2 实现的相关部分以获取想法,并将其公开。
我相信用良好的高级语言编写的程序应该很容易被只熟悉该语言的人阅读。除了上值之外,我认为 Lua 在这方面表现出色,因为它采用了类似 Pascal 的语法来表示控制结构,其含义正是人们所期望的。使用上值的程序不太可能被普通用户理解。需要手册才能理解它们何时获取其值。大多数人直观地理解静态嵌套作用域。当从嵌套函数中引用变量时,使变量不可变会给 Lua 程序的作者带来负担,但不会给程序的读者带来负担。我认为 Lua 应该设计成首先满足读者的需求。
以下是静态嵌套作用域的定义
-- John D. Ramsdell