局部变量在文件内的闭包中无法访问?

假设我有以下两个 Lua 文件:

a.lua 中:

local x = 5
f = dofile'b.lua'
f()

b.lua 中:

local fun = function()
  print(x)
end
return fun

然后如果我在 shell 中运行 luajit a.lua,它会打印 nil,因为在 b.lua 中定义的函数中看不到 x。预期的打印应该是 5。然而,如果我把所有东西都放在一个文件中,那就是我想要的:

aa.lua 中:

local x = 5
local f = function()
  print(x)
end
f()

运行 luajit aa.lua 它会打印 5

那么为什么在第一种情况下无法看到 x

点赞
用户1009479
用户1009479

如其名称所示,局部变量仅在代码块中有效。

dofile()从另一个文件中加载代码块。由于这是另一个代码块,第一个代码块中的局部变量 x 并不会影响它。

2015-10-24 23:31:41
用户3598119
用户3598119

我同意这种方法有些不直观。

你可能想说,在代码的任何时刻,有一组明确的“可见”变量--有些可能是局部的,有些可能是全局的,但有一些映射关系,解释器可以用来解决任何一种名称。

当你使用dofile加载一个块时,它可以看到当前存在的任何全局变量,但显然它无法看到任何局部变量。我们知道"dofile"不像C/C++的宏包含,后者将为本地变量提供你所描述的确切语法,但你可能合理地期望它的一部分会工作得一样。

最终没有答案,只有“这仅仅是他们指定语言的方式”。唯一令人满意的答案可能是:“否则会导致非显而易见的问题X”或“因为使用情况Y会变慢”。

我认为最好的答案是,如果使用loadfile/dofile时,所有名称都是根据它们被加载的范围动态重新绑定的,那么在将块编译为字节码时,会抑制很多优化等方面的工作。在lua系统中,名称解析的工作方式是“要么它在这个作用域中是本地的,然后它绑定到该(已知的)对象,要么它是在(唯一的)全局表中查找”。该系统非常简单,只有少数几个选项,没有太多的复杂性。

我认为即使运行字节代码也不会跟踪本地变量的名称,它会在块编译后将它们丢弃。如果他们想要在块装载时允许动态名称解析,就必须撤销那种优化。


如果你的问题不是“为什么”,而是“我该如何让它仍然工作”,则你可以在主机脚本中,将任何你想要可见的局部变量放入被调用的脚本的环境中。当你这样做时,你需要将dofile分成几个调用。在Lua 5.1和Lua 5.2中稍有不同。

在Lua 5.1中:

a.lua中:

local shared = { x = 5 }
temp = loadfile('b.lua')
setfenv(temp, shared)
f = temp()
f()

在Lua 5.2中:

a.lua中:

local shared = { x = 5 }
temp = loadfile('b.lua', 't', shared)
f = temp()
f()
2015-10-25 04:25:03
用户134758
用户134758

a.lua模块中定义的变量x无法从b.lua中看到,因为它被声明为局部变量。局部变量的作用域是它自己的模块。

如果想让xb.lua中可见,只需要声明它为全局变量。一个变量要么是局部的,要么是全局的。要将一个变量声明为全局变量,只需不将它声明为局部变量即可。

a.lua

x = 5
f = dofile'b.lua'
f()

b.lua

local fun = function()
  print(x)
end
return fun

这是有效的。

全局变量存在于全局命名空间中,可以随时通过_G表访问。当Lua无法解析变量时,因为它未在使用它的模块中定义,Lua会在全局命名空间中搜索该变量。总之,也可以将b.lua写成:

local fun = function()
  print(_G["x"])
end
return fun
2015-10-26 22:05:13