• 两个函数彻底理解Lua中的闭包


    本文通过两个函数彻底搞懂Lua中的闭包,相信看完这两个函数,应该能理解什么是Lua闭包。废话不多说,上 code:

      1 --[[***************************************************************************
      2 
      3                              I'm jacc.kim
      4 
      5     CreateDate: 2017-08-18 11:19:20 
      6     FileName  : closure.lua
      7     Version   : 1.00
      8     Author    : jacc.kim
      9     Summary   : 
     10 
     11 ***************************************************************************--]]
     12 
     13 --[[
     14  * summary     : 利用闭包实现带状态的迭代器
     15  * param       : collection 集合.
     16  * return      : !!!重点注意:函数的返回值是重点.返回值是:一!个!函!数!.而该返回的函数是一个闭包.
     17 --]]
     18 local function elementIterator(collection)
     19     -- nIndex 为 elementIterator() 函数的一个局部变量.标记着 collection 当前处理到的 collection 的下标位置 .
     20     -- 这边为每个闭包"实例"初始化它们的"成员变量"的值.(不理解这句的,可先跳过)
     21     local nIndex               = 0;
     22     -- nCollectionCount 为 elementIterator() 函数的一个局部变量.记录着命令 collection 的长度信息
     23     -- 这边为每个闭包"实例"初始化它们的"成员变量"的值.(不理解这句的,可先跳过)
     24     local nCollectionCount     = #collection;
     25     
     26     -- 闭包.
     27     -- 说明:01.在 Lua 中,函数内部可以定义函数,被定义的内部函数可以访问到外层函数定义的局部变量(如:上面的 nIndex、nCollectionCount、collection等,注意:
     28     --     外层函数的参数其实就是个局部变量).
     29     --     02.并且非常重要的(闭包的特性:)这些外层函数定义的局部变量的状态会被闭钮函数保持住.如 nIndex 起初的时候为 0 值,执行完下面的
     30     --       nIndex = nIndex + 1; 后, nIndex 的值为 1,之后,再次调用该闭包函数时, nIndex = nIndex + 1; 后, nIndex 的值就变为 2.
     31     --     03.在Lua中,函数是第一类值.因此,函数可以被赋值给一个变量(局部或全局)、可以作为函数参数传递、可以作为函数的返回值返回.因此,这边可以 return 该闭包函数.
     32     return function ()
     33         -- nIndex 为 外层函数的一个局部变量.但 nIndex 的状态会被该内部函数(可理解为闭包函数)记忆保持住.因此,每次调用该闭包函数量.nIndex的值部是不断累加的.
     34         -- (有点类似 C++ 的 static 特性(注意:仅仅只是类似,不能等同).如:
     35         -- static auto nIndex = 0;
     36         -- nIndex = nIndex + 1; // 或 ++nIndex
     37         -- )
     38         nIndex                 = nIndex + 1;    -- feature of Colsure
     39         -- nCollectionCount 为外层函数的一个局部变量.同样的该变量的状态也会被保持住.但在该闭包函数中,没有去改变 nCollectionCount 的值.因此,该值就一
     40         -- 值为集合 collection 的长度.
     41         
     42         --[[
     43         -- 现在重点分析一下这个闭包函数.在此之前,先再介绍另外一个Lua的特性.在Lua中,一个函数可以没有返回值、可以只返回一个值、也可以返回多个值.重点就是要看上下文本
     44         -- 到底需要一个函数返回多少个值出去.Ok,这点说明暂时先到此为止.下面先分析一下该闭包函数.当调用该闭包函数时,
     45            (!!!这点很重要啊)如果下面的 if 语句条件成立,则该函数会有一个返回值,返回外层函数的参数(collection)的对应索引位置处的元素.
     46            (!!!这点更加重要)如果下面的 if 语句条件不成立,则该闭包函数是没有返回值,或者根据上下文,可以返回多个的 nil 值.
     47         --]]
     48         if (nIndex <= nCollectionCount) then
     49             return collection[nIndex];
     50         end
     51     end
     52 end
     53 
     54 --[[
     55  * summary    : 测试上面设计的利用闭包实现的迭代器
     56  * param      : collection 待测试的集合.
     57  * !!!note    : 01.此处的目的:通过该函数,我们彻底理解闭包在此处起到的作用.
     58 --]]
     59 local function traversalCollection()
     60     -- collection 为测试数据.
     61     local collection           = { "apple", "pear", "orange" };
     62     -- 这是 Lua 的一个泛型 for 循环.完了完了,没办法理解该for循环?不怕,下面我们来彻底搞懂它(注意:搞懂该for循环,我们也就彻底理解什么是闭包).请看下面的注释.
     63     --[[
     64     for 循环的定义,其实可以理解为:
     65     for (条件)
     66     do
     67         // some code here........
     68     end
     69     如果for的条件成立,则会执行for后面的语句块 do ... end 中的语句.否则for循环结束.
     70     此处将下面for循环改写成另外一种"等价"的写法
     71     local iter = elementIterator(collection);
     72     for element = iter() do
     73         print(element);
     74     end
     75     或者干脆改写成 while 语句更容易理解
     76     local iter = elementIterator(collection);        -- elementIterator()函数其实就好比一个工厂一样,调用一次,产生一个闭包"实例"
     77     local element = iter();                          -- 注意:此处 iter() 就是在调用闭凶函数
     78     while element do
     79         print(element);
     80         element = iter();                            -- 注意:此处 iter() 就是在调用闭凶函数
     81     end
     82     前文已在 elementIterator() 中的闭包函数中详细说明了闭包函数的特性,闭包函数会保持外层函数的状态信息.因此,调用 iter() 函数将返回一个 collection 集合的
     83     元素,再调用一次将返回下一个元素,一直调用,最后因元素遍历结束,所以最终会返回一个nil被 element 接收.此时 while 循环结束(注意:Lua中 nil 与 false 都认为是假).
     84     至此,相当闭包大家也都理解了.
     85     --]]
     86     for element in elementIterator(collection) do
     87         print(element);
     88     end
     89 end
     90 
     91 traversalCollection();
     92 
     93 --[[
     94 上文比较神奇的地方有两处:
     95 一为 local iter = elementIterator(collection); 中的 elementIterator(collection)
     96 解释:elementIterator(collection) 有点类似一个工厂.调用一次则会 产生一个闭包"实例"(该实例"地址"被保存到 iter 变量).而该闭包"实例"拥有
     97  nIndex、nCollectionCount、collection "成员"变量.这些变量是它的自己的状态属性.
     98 如果此时再调用一次 elementIterator(collection) 则又会产生一个新的闭包"实例".每个闭包实例都将有自己的一份状态属性.这些状态属性的初始化,都是
     99 在闭包函数所在的外层函数处进行初始化(具体可见前文注释).
    100 
    101 二为  element = iter(); 中的 iter()
    102 解释:iter() 简单一点理解,其实就是在调用一个函数而已,仅此而已.然后这个函数返回一个值.只是这个函数比较特别,它有点像c++中的类一样,有自己的状态属性,调用
    103 时,它会根据自己所记录的状态属性数据,进行相应逻辑处理,并返回对应的值.
    104 
    105 总结:
    106     Lua中的闭包,说它是一个(类似c++中的)类,它不是;
    107     Lua中的闭包,说它是一个函数,它也不完全算是.只是它有点像c++中的函数,并且函数中有一些静态局部变量一样.但这样理解也不完全对.因为c++中的静态局部变量
    108     在整个程序生命周期内,仅一份实例(处于全局静态存储区中);
    109     Lua中的闭包,其实是像c++中的lambda的,但它们也不能完全等价;
    110     
    111     那么Lua中的闭包到底像什么?个人认为它与c++中的仿函数是一样的.因为它们使用上都像函数,但又都可以持有自己的状态属性数据.而且又都可以产生任意多个的"实例"112 --]]
  • 相关阅读:
    Oracle用户管理
    Oracle基本使用
    Oracle 11g安装、卸载
    Oracle
    C#面向对象
    看看Google用户体验十大设计原则
    [转]Delphi 常用控件之TlistView总结
    github + hexo 搭建博客
    CSS3 filter属性学习
    border-box——一种改变盒子尺寸的方法
  • 原文地址:https://www.cnblogs.com/tongy0/p/7388653.html
Copyright © 2020-2023  润新知