Lua迭代器与closure

预备知识
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

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,125评论 6 498
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,293评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 162,054评论 0 351
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,077评论 1 291
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,096评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,062评论 1 295
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,988评论 3 417
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,817评论 0 273
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,266评论 1 310
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,486评论 2 331
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,646评论 1 347
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,375评论 5 342
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,974评论 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,621评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,796评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,642评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,538评论 2 352

推荐阅读更多精彩内容