Lua 迭代器与泛型for
2017-06-18 17:38
281 查看
note 目录
迭代器与闭包(closure)泛型for的语义
无状态迭代器
具有复杂状态的迭代器
真正的迭代器
1:迭代器与闭包(closure)
迭代器就是一种可以遍历一种集合中所有元素的机制。迭代器通常为函数。在Lua中,通常将迭代器表示为函数。每一次调用函数,即返回集合中的”下一个元素”。每个迭代器都需要在每次成功调用之间保存一些状态,这样才能知道它所在的位置,以及下一次遍历的位置。Closure对于这类任务提供了极佳的支持。
闭包就是一个内部函数,可以访问一个或多个外部函数的局部变量。每次闭包的成功调用,这些外部的局部变量都会保存他们的值(状态)。所以如果要创建一个闭包,必须要创建其外部的局部变量。闭包必须包含2个函数,闭包的结构为:
闭包 = 闭包函数 + 工厂(创建闭包函数的工厂,有局部变量)
function values(t) --闭包工厂 local i = 0 return funciton() --闭包 (closure) i = i + 1 return t[i] end end
可以在一个
while循环中使用这个迭代器:
t = {10,20,30} iter = values(t) --创建迭代器 while true do local element = iter() --调用迭代器 if element == nil then break end print(element) end output: 10 20 30
然而使用泛型
for in则更为简单:
t = {10,20,30} for element in values(t) do print(element) end
在泛型
for in中,Lua内部帮忙我们保存了迭代器函数,因此不再需要iter变量,它在每次新迭代调用迭代器,并在迭代器返回
nil时结束循环。
2:泛型for的语义
上面的不管是while
for的example中,都有一个明显的缺点:
在每次循环时都要创建新的闭包
closure(在每次调用迭代函数
iter()),在循环很大的时候开销会比较大。
因此,在这类情况下,希望可以通过泛型for自身来保存迭代器状态。
泛型
for在循环过程内部保存了迭代器函数。实际上它保存了3个值:
一个迭代器函数,一个恒定状态(invariant state)【一般是数组或tab】,一个控制变量(control variable)【一般是数组的索引或tab的key】。
泛型
for语法
for <var-list> in <exp-list> do <body> end
[var-list]
一个或多个变量名的列表,以逗号分隔开。
[exp-list]
一个或多个表达式的列表,同样是以逗号分隔,通常表达式列表只有一个元素,即只对迭代器工厂的调用。
example:
for k , v in pairs(t) do print(k,v) end
泛型
for的执行过程:
- 1:初始化,计算
in后面的表达式的值,表达式应该返回
for所需要的3个值:迭代函数,状态常量,控制变量。与多值赋值一样。如果表达式返回的结果个数不足3个会自动补全nil,多出的部分会被忽略。
- 2:将状态常量(数组或表tab),控制变量(一般是数组的索引或tab的key)作为参数调用迭代函数。
- 3:将迭代函数返回的值赋给变量列表(var-list)。
- 4:如果返回第一个值为
nil,则循环结束,否则执行循环体。
- 5:回到第二步,再次调用迭代函数。
将泛型
for展开为
while
do local _f,_s,_val = <explist> while true do local var_1,...var_n = _f(_s,_var) _var = var_1 if _var == nil then break end end end
假设迭代函数为
f,恒定状态为
s,控制变量初值为
a0。那么在循环控制中控制变量的值依次为:
a1 = f(s,a0)
a2 = f(s,a1)
依次类推,直至ai为
nil值结束循环。如果for还有其他的变量,那么它们也会在每次调用
f后获得额外的值。
3:无状态迭代器
所谓的“无状态迭代器”,就是一种自身不保存任何状态的迭代器,因此,我们可以在多个循环体中使用同一个无状态的迭代器,避免创建新的closure开销。
每一次迭代,迭代函数都使用2个变量(状态常量和控制变量)的值作为参数被调用,一个无状态的迭代器,只利用这2个值,可以获取到下一个元素。典型的例子就是ipairs,他遍历数组的每一个元素。
--【ipairs】 a = {"one","two","three"} for i,v in ipairs a do print(i,v) end output: 1 one 2 two 3 three
【ipairs的Lua实现】
在这里,迭代的状态就是需要遍历
table(一个恒定状态,他不会在循环中改变),及当前的索引值(控制变量)。ipairs(工厂)在Lua中的实现。
local function iter(a,i) i = i + 1 local v = a[i] if v then return i , v end end function ipairs(a) return iter , a , 0 end local a = {"one","two","three"} for i , v in ipairs(a) do print(i,v) end output: 1 one 2 two 3 three
当Lua调用ipairs(a)开始循环时,他获取三个值:迭代函数iter、状态常量a、控制变量初始值0;然后Lua调用iter(a,0)返回1,a[1](除非a[1]=nil);第二次迭代调用iter(a,1)返回2,a[2]……直到第一个非nil元素。
【pairs】
函数
paris和
ipairs类似,也是遍历一个
table中的所有元素。不同的是,它的迭代器函数是Lua中的一个基本函数
next。
function pairs(t) return nex,t,nil end
在调用
next(t,k)时,
k是
tablet的一个
key,此调用会以
table中的任意次序返回一组值:
table的下一个’key’,及这个
key对应的值。而调用
next(t,nil)时,返回
table的第一组值。
若没有下一组值时,
next返回
nil。
–还可以不使用ipairs直接使用next
for k, v in next, c do print(k,v) end
4:具有复杂状态的迭代器
很多情况下,迭代器需要保存多个状态信息而不是简单的状态常量和控制变量,最简单的方法是使用闭包,还有一种方法就是将所有的状态信息封装到table内,将table作为迭代器的状态常量,因为这种情况下可以将所有的信息存放在table内,所以迭代函数通常不需要第二个参数。尽可能地尝试编写无状态的迭代器,那些迭代器将所有的状态保存在for变量中,不需要在开始一个循环时创建任何新的对象。
如果迭代器无法套用这个模型,那么应该尝试使用
closure,
closure显得跟家优雅些,通常一个基于
closure实现的迭代器会比创建一个
table更廉价,其次访问“非局部变量”也比访问
table字段更快。
协同程序(coroutine)可以用来别写迭代器。
5:真正的迭代器
迭代器的名字有一些误导,因为它并没有迭代,完成迭代功能的是for语句,也许更好的叫法应该是生成器(generator)有一种方式创建一个在内部完成迭代的迭代器。这样当我们使用迭代器的时候就不需要使用循环了;我们仅仅使用每一次迭代需要处理的任务作为参数调用迭代器即可,具体地说,迭代器接受一个函数作为参数,并且这个函数在迭代器内部被调用。
funciton allwords(f) for line in io.lines() do for word in string.gmatch(line,"%w+") do f(word) --call the function end end end
如果我们只想打印单词
allwrods(print)
更一般的做法是使用匿名函数作为参数。
local count = 0 allwords(function (w) if w == "hello" then count = count + 1 end end) print(count)
真正的迭代器风格的写法在Lua老版本中很流行,那时还没有for循环。
两种风格的写法相差不大,但也有区别:一方面,第二种风格更容易书写和理解;另一方面,for结构更灵活,可以使用break和continue语句;在真正的迭代器风格写法中return语句只是从匿名函数中返回而不是退出循环。
相关文章推荐
- lua中的泛型for 和迭代器
- lua迭代器与泛型for
- Lua迭代器和泛型for)
- lua中的迭代器与泛型for
- Lua学习笔记(七):迭代器与泛型for
- lua语言-《lua程序设计》6~7章函数和迭代器与泛型for
- Lua学习笔记 第七章 迭代器与泛型for
- 二、Lua迭代器与泛型for
- Lua学习笔记-迭代器和泛型for
- 每日一Lua(5)-迭代器与泛型for
- Lua迭代器与泛型for
- Step By Step(Lua迭代器和泛型for)
- 六、Lua中的迭代器与泛型for
- Lua中的迭代器和泛型for介绍
- 利用lua中的closure来实现lua迭代器以及用泛型for实现lua的无状态迭代器
- 每日一Lua(5)-迭代器与泛型for
- Lua学习(七)-----迭代器和泛型for
- Lua笔记7-迭代器和泛型for
- Step By Step(Lua迭代器和泛型for)
- 深入解读Lua中迭代器与泛型for的使用