美文网首页
Lua迭代器与closure

Lua迭代器与closure

作者: 凉拌姨妈好吃 | 来源:发表于2018-06-27 09:13 被阅读0次

    预备知识
    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在循环过程中保存了三个值:迭代器函数、恒定变量、控制变量

    泛型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

    相关文章

      网友评论

          本文标题:Lua迭代器与closure

          本文链接:https://www.haomeiwen.com/subject/zlemyftx.html