预备知识:
lua函数是一种First-Class Value,即它与传统的变量并没有什么区别。
它可以存储到变量中、存储到table中、作为实参传递给其他函数、作为其他函数的返回值等。
它们还具有特定的词法域,也就是说,一个函数可以嵌套在另一个函数中,内部函数可以访问外部函数的变量。
1. closure(闭合函数)
1.1 closure的组成
一个closure就是一个函数加上这个函数所需访问的所有"非局部变量",非局部变量也就是外部函数的变量
function newCounter()
local i = 0
func = function()
i = i + 1
return i
end
return func
end
c1 = newCounter()
print(c1())
print(c1())
当我们每次再调用newCounter这个函数的时候,也会得到一个新的closure,这往往是性能损耗的关键。
1.2 upValue
每个闭包都可以有一个upValue值,多个闭包可以共享一个upValue数值。
外部函数的局部变量,因为它已经是内部函数的upValue,所以局部变量的生命期延长。当我们不再调用函数,那么闭包就不会再生成,那么此时局部变量的生命期就到了尽头,等待gc回收。
upValue其实就是局部变量,当它还没有离开它的作用域,它就一直生存在堆栈上,闭包通过指向堆栈的引用了来访问它们,一旦upValue即将离开自己的作用域,在从堆栈消除之前,闭包就会为它分配空间并保留当前的值。
1.3闭包生成流程
每当Lua执行一个函数时,它就会创建一个新的数据对象,这个数据对象包含了相应函数原型的引用、环境的引用以及一个由所有upValue引用组成的数组,这个数据对象就称为闭包。
2. 迭代器
2.1 为什么需要迭代器
编程中我们往往碰到各种各样的容器,不一样的容器它们的底层代码实现是不同的,那就意味着,遍历它们需要不同的方式。这样一来非常不利于代码重用,所以迭代器就出现了,我们将遍历容器的操作都封装在迭代器里,那么我们就不需要考虑这个容器需要用哪一种遍历方式。
2.2 什么是迭代器
它是一种可以遍历一种集合所有元素的机制。Lua中迭代器常意味着函数,每调用一次函数,即返回集合的下一个元素。
2.3 迭代器的使用
每个迭代器都需要在每次成功调用之前保持一些状态,这样才知道它的位置以及如何进入下一步位置。
2.4 用closure方式保持状态
一个closure就是一种可以访问外部嵌套环境的局部变量的函数,这些变量可以用在成功调用之后保持状态值,从而closure可以记住它在一次遍历后的位置。一个closure结构通常涉及两个函数,closure自身和用于创建该closure的工厂函数。
下图的values就是一个工厂函数,当我们每次调用它的时候都会新建一个closure,这个closure就会将它的状态保存到外部变量i和t中,此时i和t因为closure,生命期延长,在函数结束后不会立马消失。所以每当调用values时,都会从列表t返回下一个值,直到最后一个元素返回后,迭代器返回nil,至此迭代结束。
closure
2.5 用泛型for保持状态
它和上面的closure相比,效率提高了许多,因为不需要每一次调用的时候都去创建一个新的closure。
泛型for在循环过程中保存了三个值:迭代器函数、恒定变量、控制变量
下图k,v便是变量列表,也就是上图的var-list。变量列表的第一元素为"控制变量",循环过程中该值不会为nil,当它为nil时,循环就结束了。
2
我们看一下下面的泛型for实例,ipairs2也就是我们的表达式的原型,它返回迭代器函数、恒定对象、调用iter函数为其传入的初始值。迭代器iter返回值会赋予变量列表中的变量,如图为i,v赋给k,v,如果返回值i为nil,那么循环终止。
泛型for实例
2.6 无状态的迭代器
无状态迭代器是不保留任何状态的迭代器,避免创建闭包花费额外的代价。其实上面我们使用的ipairs2就是有一个无状态的迭代器。
下面调用square的流程就是square(3,0),square(3,1),square(3,2),它将3作为恒定状态,0作为迭代器函数的初始值。
function square(iteratorMaxCount,currentNumber)
if currentNumber<iteratorMaxCount
then
currentNumber = currentNumber+1
return currentNumber, currentNumber*currentNumber
end
end
for i,n in square,3,0
do
print(i,n)
end
2.7 多状态的迭代器
当我们迭代器需要保存多个状态时,泛型for就不适用了,因为泛型for只提供一个恒定状态和控制变量用于状态的保存。
有两种方法可以实现多状态迭代器:closure、将所需状态打包为一个table
网友评论