如何在lua中获得关闭?

时间:2009-05-22 13:47:41

标签: lua closures

假设我有一个文件名“test.lua”,其中包含以下行:

--[[  test.lua --]]
local f = function()
  print"local function f in test.lua"
end

f_generate = function()
  local fun = loadstring(" f()")
-- local env = getfenv(1)
-- set(fun,env)
  return fun
end
f_generate()()
--[[ end of test.lua--]]

因为loadstring在全局环境下执行其操作,所以当我调用时   f_generate()() 我将收到一个错误“尝试调用全局'f'(零值)”

注释掉的代码表明功能环境无法解决这个问题。

因为table是lua中唯一的数据结构,(函数环境和其他很多东西都是通过表实现的),我认为假设闭包也是通过表实现是合理的,但我怎么能得到它呢?

4 个答案:

答案 0 :(得分:4)

从提出的问题和提供的示例代码中,当函数和闭包是该语言中的第一类值时,我认为不需要使用loadstring()。我会考虑这样做:

-- test.lua 
local f = function()
  print"local function f in test.lua"
end

f_generate = function()
  local fun = function() return f() end
  return fun
end
f_generate()()
-- end of test.lua

如果f_generate有参数,那么动机会更清楚:

-- test.lua 
local f = function(y)
  print("local function f("..y..") in test.lua")
end

f_generate = function(name)
  local fun = function() return f(name) end
  return fun
end
f_generate("foo")()
f_generate("bar")()
-- end of test.lua

使用loadstring()的解析器显式地将代码调用到loadstring()的调用范围之外。局部变量不存储在任何环境表中。它们的实现方式与任何其他语言的实现方式大致相同:它们的存储由代码生成器分配,在编译之外无法访问。当然,调试模块(和API)能够查看它们,但绝不建议在调试器之外使用它。

保留对本地的引用以在范围之外使用的正确方法是作为闭包的真正upvalue。这就是fun = function() return f() end所取得的成就。在这种情况下,值f将保留为fun中存储的函数的upvalue。请注意,在实践中,这种包装作为upvalue是非常有效的。查找值不需要名称查找,因为我使用了尾调用,所以也不需要额外的堆栈帧。

答案 1 :(得分:0)

我认为你混合了两件不同的东西:

  1. 闭包:为此,f()的定义应该在你想要包含的任何局部变量的范围内。

    • 将局部变量插入动态编译的函数中。这不是Lua正式支持的。这不是环境问题,而是范围问题。
  2. 记住:范围是词汇,而环境是每个功能都认为是“全球空间”的。

    由文本字符串构造的函数位于不同的词法空间中,就像它位于不同的文件中一样,因此它具有自己的范围,与其他函数分开。

    顺便说一句,'debug'界面让你用一个函数的局部变量进行处理,所以可能有办法。我只是觉得不需要这样做。

答案 2 :(得分:0)

您必须删除“本地”,否则将被垃圾收集。

--local f = function()
f = function()
  print"local function f in test.lua"
end

答案 3 :(得分:0)

请参阅,您不能将函数/闭包视为表。请考虑以下代码:

local table = {
    baz = {
        blah = "bar"
    },
    foo = table.baz.blah
}

在这种情况下,您执行的操作相当于从更广泛的范围访问更窄范围内的内容。对于函数来说这是不可能的,这意味着如果这是真的,那么你可以访问通常不能的局部变量。

现在,修复你的代码:

local __cmp__table = { 
    [">"] = function(a,b) return a>b end, 
    [">="] = function(a,b) return a>=b end, 
    ["<"] = function(a,b) return a<b end, 
    ["<="] = function(a,b) return a<=b end, 
    ["=="] = function(a,b) return a==b end, 
    ["~="] = function(a,b) return a~=b end, 
} 
cmp = function(a, op, b) 
    return __cmp__table[op](a,b) 
end

这将允许您使用适当的比较函数在任何两个变量上调用cmp。如果我错过了关于你的代码的观点,那么请告诉我!