如何在 Lua 中获得闭包?

假设我有一个名为"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'(一个空值)"

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

因为表是lua中唯一的数据结构,(因为函数环境和其他很多东西都是由表实现的),我认为假设闭包也是由表实现的是合理的,但是如何获得它呢?

原文链接 https://stackoverflow.com/questions/897930

点赞
stackoverflow用户11649
stackoverflow用户11649

我认为您正在混淆两件不同的事情:

  1. 闭包:为此,f()的定义应在您想要包含的任何局部变量的作用域内。
  • 将局部变量插入动态编译的函数。 这不是Lua正式支持的。 这不是一个环境问题,而是一个范围问题。

记住:范围是词法的,而环境是每个函数认为的“全局空间”。

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

顺便说一下,'debug'接口让您干预函数的局部变量,因此可能有一种方法。 我只是没有感到有必要这样做。

2009-05-22 14:03:12
stackoverflow用户108130
stackoverflow用户108130

你必须移除 'local' 否则它将被垃圾回收。

--local f = function()
f = function()
  print"local function f in test.lua"
end
2009-05-22 14:10:48
stackoverflow用户68204
stackoverflow用户68204

从提出的问题和提供的示例代码来看,在该语言中有函数和闭包作为第一类值的情况下,我不认为需要使用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)可以查看它们,但是不推荐在调试器之外使用它们。

保留用于范围外部使用的本地变量的正确方法是作为闭包的真正上值。这就是通过fun = function() return f() end实现的。这种情况下,值f被保留为存储在fun中的函数的上值。请注意,实际上,将其包装为上值是非常有效的。不需要查找名称以查找该值,而且由于我使用了尾调用,因此也不需要额外的堆栈帧。

2009-05-22 20:56:05
stackoverflow用户117069
stackoverflow用户117069

看吧,你不能把函数/闭包当作表来处理。考虑以下代码:

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 。如果我错过了你关于代码的重点,请告诉我!

2009-08-24 02:58:36