有一些关于Lua中“Closure”的问题。

这是我的代码,我混淆了返回函数(c1,c2)中的局部变量“count”与内存栈,并且它们存储在哪里?

function make_counter()
  local count = 0
  return function()
    count = count + 1
    return count
  end
end
c1 = make_counter()
c2 = make_counter()
print(c1())--打印->1
print(c1())--打印->2
print(c1())--打印->3

print(c2())--打印->1
print(c2())--打印->2

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

点赞
stackoverflow用户734069
stackoverflow用户734069

我不太确定您确切想问什么,但我会尝试解释一下闭包的工作原理。

当您在 Lua 中执行以下操作时:

function() <some Lua code> end

您正在创建一个_值_。值是像数字 1,字符串“string”等东西。

值是_不可变的_。例如,数字1始终是数字1。它永远不能是数字2。您可以将1加到2,但这将给您一个新的数字3。对于字符串也是如此。字符串“string”是一个字符串,将始终是该特定字符串。您可以使用 Lua 函数从字符串中删除所有“g”字符,但这将创建一个_新_字符串“strin”。

函数是_值_,就像数字1和字符串“string”一样。值可以存储在变量中。您可以在多个变量中存储数字1。您可以在多个变量中存储字符串“string”。也适用于所有其他类型的值,包括函数。

函数是值,因此它们是不可变的。但是,函数可以_包含_值;这些值是_不_不可变的。这很像表格。

{}语法创建一个 Lua 表,这是一个值。这张表与任何其他表格都不同,甚至是其他空表。但是,您可以在表格中放入不同的东西。这不会改变表的唯一值,但它确实会改变存储在该表中的内容。每次执行{}时,都会得到一个新的唯一表。所以如果你有以下函数:

function CreateTable()
    return {}
end

以下是正确的:

tableA = CreateTable()
tableB = CreateTable()
if(tableA == tableB) then
    print("You will never see this")
else
    print("Always printed")
end

即使tableAtableB都是空表格(包含相同的东西),它们也是_不同_的表格。它们可能包含相同的内容,但它们是不同的值。

函数也是如此。在 Lua 中,函数通常被称为“闭包”,特别是如果函数具有内容。根据变量使用情况,函数的内容将给出。如果函数引用在创建该函数的位置处的范围内的局部变量(记住:语法function()end每次调用都会创建一个函数_),则该函数将包含对该局部变量的引用。

但是,局部变量会超出范围,而函数的值可能会继续存在(在您的情况下,您会返回它)。因此,函数的对象,闭包,必须包含对该局部变量的引用,从而导致它继续存在,直到闭包本身被丢弃。

值存储在哪里?没关系;只有闭包可以访问它们(尽管有通过 C Lua API 或 Lua Debug API 的方法)。因此,与表不同,您可以获取任何您想要的内容,闭包可以真正隐藏数据。

2011-08-04 03:31:00
stackoverflow用户139010
stackoverflow用户139010

在带有内存堆栈的返回函数 c1,c2 中,它们存储在闭包中! c1 不是闭包,它是通过make_counter() 返回的函数。闭包没有在任何地方明确声明。它是make_counter()返回的函数和该函数的“自由变量”的组合。参见 closures @ Wikipedia,特别是 implementation

闭包通常使用一个特殊的数据结构来实现,该结构包含对函数代码的指针,以及在创建闭包时的函数的词法环境的表示(例如,可用变量及其值的集合)。

2011-08-04 03:57:29
stackoverflow用户1946344
stackoverflow用户1946344

Lua 闭包还可以用来实现基于原型的类和对象。闭包类和对象的行为与普通的 Lua 类有些微小的差别,它们的调用方式也略有不同:

-- 定义闭包类

StarShip = {}

function StarShip.new(x,y,z)
    self = {}

    local dx, dy, dz
    local curx, cury, curz
    local engine_warpnew

    cur_x = x; cur_y = y; cur_z = z

    function setDest(x,y,z)
        dx = x; dy=y; dz=z;
    end

    function setSpeed(warp)
        engine_warpnew = warp
    end

    function self.warp(x,y,z,speed)
        print("warping to ",x,y,x," at warp ",speed)
        setDest(x,y,z)
        setSpeed(speed)
    end

    function self.currlocation()
        return {x=cur_x, y=cur_y, z=cur_z}
    end

    return self
end

enterprise = StarShip.new(1,3,9)

enterprise.warp(0,0,0,10)

loc = enterprise.currlocation()

print(loc.x, loc.y, loc.z)

将产生如下输出:

warping to 0 0 0 at warp 10 1 3 9

这里我们定义了一个原型对象“StarShip”,它是一个空表。

然后,我们创建一个 StarShip 的构造函数,在“new”方法中。它所做的第一件事是创建一个名叫 self 的闭包表,其中包含对象的方法。闭包中的所有方法(在“function self.”中定义的)都对构造函数可访问的所有值进行了“封闭”或定义。这就是为什么它叫做闭包。当构造函数完成时,它将返回闭包对象“return self”。

关于基于闭包的对象的更多信息,请参见:

http://lua-users.org/wiki/ObjectOrientationClosureApproach

2013-01-03 18:09:34