TheRiver | blog

You have reached the world's edge, none but devils play past here

0%

lua-闭包

引用

programming in lua

The Implementation of Lua5.0.pdf

函数和闭包

当 Lua 编译一个函数时,会生成一个原型。该原型包含有函数的虚拟机指令 、
常数值(数值、字符串等),以及一些调试信息。在运行期,任何时候只要 Lua
执行一个 function...end 表达式,它就会创建一个新的闭包。每个闭包都有一个对
函数原型的引用、一个对环境的引用(环境其实是一个表,函数可在该表中索引
全局变量,后面细述),和一个数组,数组中每个元素都是一个对 upvalue 的引
用,可通过该数组来存取外层的局部变量。

lua的函数

lua的函数是带有词法界定第一类值

词法界定:被嵌套的函数可以访问它外部的局部变量

LUA

function newCounter()
local i = 0;
return function()
    i = i + 1;
    return i;
end
end

c1 = newCounter();
print(c1());
print(c1());

C/C++

int function()
{
    i = i + 1;
    return i;
}

int newCounter()
{
    int i = 0;
    return function();
}

int main()
{
    cout << newCounter() << endl;
    return 0;
}

error: 'i' was not declared in this scope|

第一类值:在lua中函数和其他值(数值,字符串)一样,函数可以被存放在变量中,也可以存放在表中,可以作为函数的参数,还可以作为函数的返回值.

upvalue

在内部函数变量i既不是全局变量,也不是局部变量,这种称为外部局部变量或者upvalue.

upvalue.png

Lua 用一种称为 upvalue 的结构来实现闭包。对任何外层局部变量的存取间
接地通过 upvalue 来进行。upvalue 最初指向栈中变量活跃的地方(图 4 左 边 )。
当离开变量作用域时(超过变量生存期时),变量被复制到 upvalue 中(图 4 右
边)。由于对变量的存取是通过 upvalue 里的指针间接进行的,因此复制动作对
任何存取此变量的代码来说都是没有影响的。与内层函数不同的是,声明该局部
变量的函数直接在堆栈中存取它的局部变量。

通过为每个变量至少创建一个 upvalue 并按所需情况进行重复利用,保证了
未决状态(是否超过生存期)的局部变量(pending vars)能够在闭包间正确地
共享。为了保证这种唯一性,Lua 为整个运行栈保存了一个链接着所有正打开着
的 upvalue(那些当前正指向栈内局部变量的 upvalue)的链表(图 4 中未决状态
的局部变量的链表)。当 Lua 创建一个新的闭包时,它开始遍历所有的外层局部
变量,对于其中的每一个,若在上述 upvalue 链表中找到它,就重用此 upvalue,
否则,Lua 将创建一个新的 upvalue 并加入链表中。注意,一般情况下这种遍历
过程在探查了少数几个节点后就结束了,因为对于每个被内层函数用到的外层局
部变量来说,该链表至少包含一个与其对应的入口(upvalue)。一旦某个关闭的
upvalue 不再被任何闭包所引用,那么它的存储空间就立刻被回收。

一个函数有可能存取其更外层函数而非直接外层函数的局部变量。这种情况
下,有可能当闭包创建时,此局部变量尚不存在。Lua 使用 flat 闭包来处理这种
情况。有了 flat 闭包,无论何时只要函数存取更外层的局部变量,该变量也会进
入其直接外层函数的闭包中。这样,当一个函数被实例化时,所有进入其闭包的
变量就在直接外层函数的栈或闭包中了。

ending

road_small.jpg

----------- ending -----------