本文通过两个函数彻底搞懂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 = ;
  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 + ; -- 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. --]]

两个函数彻底理解Lua中的闭包的更多相关文章

  1. [译] Closures in Lua - Lua中的闭包

    原文:(PDF) . 摘要 一等(first-class)函数是一种非常强大的语言结构,并且是函数式语言的基础特性.少数过程式语言由于其基于栈的实现,也支持一等函数.本文讨论了Lua 5.x用于实现一 ...

  2. Lua中的闭包

    [什么是闭包?] 闭包在Lua中是一个非常重要的概念,闭包是由函数和与其相关的引用环境组合而成的实体.我们再来看一段代码: function newCounter() return function ...

  3. [转][译] Closures in Lua - Lua中的闭包

    http://www.cnblogs.com/plodsoft/p/5900270.html?utm_source=tuicool&utm_medium=referral 原文:(PDF) . ...

  4. 浅谈 .NET 中的对象引用、非托管指针和托管指针 理解C#中的闭包

    浅谈 .NET 中的对象引用.非托管指针和托管指针   目录 前言 一.对象引用 二.值传递和引用传递 三.初识托管指针和非托管指针 四.非托管指针 1.非托管指针不能指向对象引用 2.类成员指针 五 ...

  5. 理解lua中 . : self

    前言 在LUA中,经常可以看到:. self,如果你学习过Java或C#语言,可以这样理解 .对于c#和java的静态方法 :相当于是实例方法 今天在CSDN上看到一篇博客写的很清楚,转载过来 原文出 ...

  6. 全面理解JavaScript中的闭包的含义及用法

    1.什么是闭包 闭包:闭包就是能够读取其他函数内部变量的函数;闭包简单理解成“定义在一个函数内部的函数”. 闭包的形式:即内部函数能够使用它所在级别的外部函数的参数,属性或者内部函数等,并且能在包含它 ...

  7. 深入理解JavaScript中的闭包

    闭包没有想象的那么简单 闭包的概念在JavaScript中占据了十分重要的地位,有不少开发者分不清匿名函数和闭包的概念,把它们混为一谈,我希望借这篇文章能够让大家对闭包有一个清晰的认识. 大家都知道变 ...

  8. lua中的闭包概念的学习笔记

    1.闭包的由来: 个人理解,lua中之所以出现闭包的概念,完全是因为lua中允许函数的嵌套定义,并且在内嵌函数中使用了外包函数中定义的局部变量,例如c.c#就不允许函数的嵌套定义(但是允许函数的嵌套调 ...

  9. 【译】理解Rust中的闭包

    原文标题:Understanding Closures in Rust 原文链接:https://medium.com/swlh/understanding-closures-in-rust-21f2 ...

随机推荐

  1. 5、Linux操作系统介绍

    1操作系统的作用·是现代计算机系统中最基本和最重要的系统软件·是配置在计算机硬件上的第一层软件,是对硬件系统的首次扩展·主要作用是管理好硬件设备,并为用户和应用程序提供一个简单的接口,以便于使用·而其 ...

  2. spring boot 加载原理

    spring boot quick start 在springBoot里面,很吸引的一个特征就是可以直接把应用打包成jar/war包形式.然后jar/war包可以直接运行的.不需要再配置web Ser ...

  3. spring-boot 属性定义和配置bean

    自定义bean属性 1.定义bean属性 // 通过@ConfigurationProperties加载properties文件内的配置, // 通过prefix属性指定properties的配置的前 ...

  4. BBScan — 一个信息泄漏批量扫描脚本

    github:https://github.com/lijiejie/BBScan 有些朋友手上有几十万甚至上百万个域名,比如,乌云所有厂商的子域名. 如果把这30万个域名全部扔给wvs,APPsca ...

  5. vscode中go插件配置

    # 转自:http://www.mamicode.com/info-detail-2436665.html # https://blog.csdn.net/bing2011/article/detai ...

  6. SYN Flood攻击及防御方法 (转)

    原文连接:http://blog.csdn.net/bill_lee_sh_cn/article/details/6065704 一.为什么Syn Flood会造成危害      这要从操作系统的TC ...

  7. C++ 和Java继承机制的比较

    摘要: C++支持类的多继承,而Java采用类的单继承.C++中的继承成分只有类(模板属于带参数的类,结构和联合是特殊的类),Java中除了类还有接口的继承,而且允许接口的多继承,可以间接地实现类多继 ...

  8. web.xml中的dispatchservlet后,js,css,甚至gif都不能正常显示

    这个可以说是很多初学Springmvc的人都会碰到一个令人头痛的问题 那就是为什么我配置好web.xml中的dispatchservlet后,js,css,甚至gif都不能正常显示了 我们来看看我们配 ...

  9. HDU 1686 Oulipo(KMP变形求子串出现数目(可重))

    题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=1686 题目大意:给两个字符串A,B求出A中出现了几次B(计算重复部分). 解题思路:稍微对kmp()函 ...

  10. HDU 1079 Calendar Game(博弈找规律)

    题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=1079 题目大意:给你一个日期(包含年月日),这里我表示成year,month,day,两人轮流操作,每 ...