【问题标题】:local variable cannot be seen in a closure across the file?在文件的闭包中看不到局部变量?
【发布时间】:2015-10-30 22:20:35
【问题描述】:

假设我有以下两个 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

【问题讨论】:

    标签: lua


    【解决方案1】:

    顾名思义,局部变量是块的局部变量。

    dofile() 从另一个文件加载块。因为它是另一个块,所以它看不到第一个块中的局部变量x 是有道理的。

    【讨论】:

    • 感谢您的回答!还有一件事:如果函数 fun 的主体太大,我不想把它放在文件 a.lua 而是一个单独的文件中(就像我在第一种情况下所做的那样不起作用),有什么好的成语让它起作用吗?
    【解决方案2】:

    我同意这不起作用有点不直观。

    您想说,在代码中的任何位置,都有一组明确的“可见”变量——有些可能是本地的,有些可能是全局的,但解释器可以使用一些映射解析任何一种名称。

    当您使用 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()
    

    【讨论】:

    • 感谢您漫长而耐心的解释!只是一个相关的问题,我也在另一个答案下面发布:如果函数 fun 的主体太大,我不想把它放在文件 a.lua 而是一个单独的文件中(就像我在第一个不起作用的情况),有什么好的成语让它起作用吗?
    • @pengsun:我想我的建议是尝试将整个函数重写和重构为许多小函数。这个 lua 风格指南建议避免使用大型函数,并且还有一篇关于它的博客文章。 github.com/Olivine-Labs/lua-style-guide#functions 我不认为 lua 对制作“包含宏”有很好的支持,就像你建议的那样。
    【解决方案3】:

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

    如果您想让xb.lua 可见,只需声明它全局。变量要么是local,要么是global。要将变量声明为 global,只需不要将其声明为 local

    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
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-07-21
      • 1970-01-01
      • 2016-09-02
      • 1970-01-01
      • 2011-01-21
      • 2018-12-24
      相关资源
      最近更新 更多