lua闭包全面解析

Lua中,闭包(closure)是由一个函数和该函数会访问到的非局部变量(或者是upvalue)组成的,其中非局部变量(non-local variable)是指不是在局部作用范围内定义的一个变量,但同时又不是一个全局变量,主要应用在嵌套函数和匿名函数里,因此若一个闭包没有会访问的非局部变量,那么它就是通常说的函数。也就是说,在Lua中,函数是闭包一种特殊情况简而言之,闭包就是一个函数加一个upvalue。那么接下来看下upvalue是啥。

Lua使用结构体upvalue来实现闭包。外面的局部变量可以直接通过upvalue进行访问。upvalue最开始的时候指向栈中的一个变量,此时这个变量还在它的生存周期内。当变量离开作用域(译者注:就是函数返回后,变量的生存周期结束时),这个变量就从栈转移到了upvalue中。虽然这个变量存储在upvalue中,但是访问这个变量还是间接通过upvalue中的一个指针进行的(译者注:和在栈中时候的访问方式一样)。因此,变量位置的转移对任何试图读写这个变量的代码都是透明的。有别于这个变量在一个函数内部时候的行为,函数声明、访问这个变量,就是直接对栈的操作。看下具体例子:

function f1(n)
   --函数参数n也是局部变量
   local function f2()
      print(n)   --引用外部函数的局部变量
   end
   return f2
end

g1 = f1(2015)
g1() -- 打印出2015

g2 = f1(2016)
g2() -- 打印出2016

这里的n就是upvalue。 upvalue 实际指的是变量而不是值,这些变量可以在内部函数之间共享,即 upvalue 提供一种闭包之间共享数据的方法,再看个例子:

function Create(n)
   local function foo1()
      print(n)
   end
   local function foo2()
      n = n + 10
   end
   return foo1,foo2
end

f1,f2 = Create(2015)
f1() -- 打印2015

f2()
f1() -- 打印2025

f2()
f1() -- 打印2035

上面的例子中,闭包f1f2共享同一个upvalue了,这是因为当Lua发现两个闭包的upvalue指向的是当前堆栈上的相同变量时,会聪明地只生成一个拷贝,然后让这两个闭包共享该拷贝,这样任一个闭包对该upvalue进行修改都会被另一个探知。为什么会这样,我们看下面的解释:

通过为每个变量最多创建一个upvalue并按需要重复利用这个upvalue保证了未决状态(未超过生命周期)的局部变量(pending vars)能够在闭包之间正确地共享。为了保证这种唯一性,Lua维护这一条链表,该链表中每个节点对应一个打开的upvalueopend upvalue)结构,打开的upvalue是指当前正指向栈局部变量的upvalue,如上图的未决状态的局部变量链表(the pending vars list)。当Lua创建一个新的闭包时,Lua会遍历当前函数所有的外部的局部变量,对于每一个外部的局部变量,若在上面的链表中能找到该变量,则重复使用该打开的upvalue,否则,Lua会创建一个新的打开的upvalue,并把它插入链表中。当局部变量离开作用域时(即超过变量生命周期),这个打开的upvalue就会变成关闭的upvalueclosed upvalue),并把它从链表中删除,一旦某个关闭的upvalue不再被任何闭包所引用,那么它的存储空间就会被回收。

最后看下闭包的应用。闭包最常用的一个应用就是实现迭代器。所谓迭代器就是一种可以遍历一种集合中所谓元素的机制。每个迭代器都需要在每次成功调用之间保持一些状态,这样才能知道它所在的位置及如何进到下一个位置。闭包刚好适合这种场景。比如下面的代码:

function values(t)
	local i = 0
	return function () i = i + 1 return t[i] end
end

t = {10, 20, 30}

iter = values(t)
while true do
	local element = iter()
	if element == nil then break end
	print(element)
end

总结下lua闭包,关键点是upvalue,然后注意下如何申明一个背包,函数(A)里面返回的是函数(B),B引用了A的局部变量。


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值