加入收藏 | 设为首页 | 会员中心 | 我要投稿 李大同 (https://www.lidatong.com.cn/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 大数据 > 正文

Lua学习笔记(三)

发布时间:2020-12-14 21:55:28 所属栏目:大数据 来源:网络整理
导读:八、迭代器与泛型for 迭代器是一种可以遍历一种集合中所有元素的机制。在Lua中,迭代器用函数表示,每调用一次函数即返回集合中的“下一个”元素。 1、迭代器与closure 每个迭代器都需要在每次成功调用之间保持一些状态,这样才能知道它所在位置及如何到下一

八、迭代器与泛型for

迭代器是一种可以遍历一种集合中所有元素的机制。在Lua中,迭代器用函数表示,每调用一次函数即返回集合中的“下一个”元素。

1、迭代器与closure

每个迭代器都需要在每次成功调用之间保持一些状态,这样才能知道它所在位置及如何到下一个位置。closure为此提供了支持。因此迭代器遍历集合需要一个closure和创建该closure的工厂函数。

例如一个用于遍历数组的工厂函数(返回一个迭代器函数):

function values(t)?? --t is a table

?????? local i=0

?????? return function () i=i+1;return t[i] end

end

values()是一个工厂,返回的函数用于遍历一个table。每当调用values函数就创建一个新的closure(包括迭代函数和相关的非局部的变量)。

使用values有以下方法:

①while

t={10,20,30}

it=values(t)??? --???? 创建迭代器

while true do

?????? local element=it()?? --调用迭代器获得集合的值

?????? if element == nil then break

?????? else print(element)

?????? end

end

②泛型for

for element invalues(t) do

?????? print(element)

end

泛型for在内部保存了迭代函数,因此不需要显式地指定一个values返回值(it)。

2、泛型for的语义

对于上述while的循环,每次调用迭代器都要创建一个新的闭包(每执行一次closure就不同了???)。在某些情况下,这种开销是不允许的。而泛型for自身保存了迭代器状态。

泛型for循环保存了3个值:迭代器函数、状态常量(invariant state)、控制变量(control variable)。

泛型for的语法如下:

for<var-list> in <exp-list> do

?????? <body>

end

var-list是一个或多个变量列表。exp-list是一个或多个表达式列表,通常只含一个元素即对工厂函数的调用。

变量列表的第一个元素称为“控制变量”。

泛型for执行过程如下:

①对exp-list求值,得到3个值,即迭代器函数、状态常量、控制变量。exp-list与多重赋值一样,返回多个值时只保留前三个,并且只有最后一个表达式可获得多个结果。不足三个时补nil。

②初始化后用状态常量和控制变量调用迭代器函数——对于 for 结构来说,状态常量没有用处,仅仅在初始化时获取他的值并传递给迭代函数。

③将迭代器函数的返回值赋给var-list。

④如果第一个返回值为nil则结束循环,否则执行循环体。

⑤执行第②步,用新的控制变量(③得到的)作为参数调用迭代函数。

总的来说,用语句表示如下:

for var_1,…,var_nin <exp-list> do <block> end

等同于

do

local _f,_s,_var=<exp-list>

?????? while ture do

????????????? local var_1,var_n = _f(_s,_var)

????????????? _var = var_1

????????????? if _var ==nil then break end

????????????? <block>

?????? end

end

_f,_var分别表示迭代器函数、状态常量、控制变量。在用_f对变量列表初始化后,将第一个变量赋值给控制变量,当控制变量为nil时结束循环。

3、无状态的迭代器(泛型for)

正如上一节介绍的泛型for机制,for来遍历table只用到了它自带的三个变量来实现。这种自身不保存任何状态的迭代器称为“无状态的迭代器”。这样可以在多个for循环中使用同一个“无状态的迭代器”,避免创建新的closure。

ipairs就是一个例子:

local functioniter(a,i)

?????? i=i+1

?????? local v=a[i]

?????? if v then

????????????? return i,v

?????? end

end

--ipairs函数实现

function ipairs(a)

?????? return iter,a,0

end

????? ipairs返回了迭代器函数(iter)、状态常量(a,即要遍历的table)、控制变量的初值(0)。通过这三个变量就可以完成对table的遍历。是通过保存table和return i并作为遍历下一个元素的参数传入迭代器函数的方式来遍历的,不需要创建closure。

????? pairs函数的迭代器函数是一个基本函数next:

?????? function pairs(t)

????????????? return next,t,nil

?????? end

????? 调用next(t,k)时,k是table t的一个key,返回任意的一组值:此table的下一个key和对应的value。而调用next(t,nil)时返回第一组值(key-value),没有下一组时next返回nil。

????? 可以直接使用next来遍历,而不用pairs:

????? for k,v in next,t do --_f=next,_s=t,_var=nil

????????????? <block>

?????? end

4、复杂状态的迭代器

泛型for只能提供状态常量和控制变量来保存状态,不能满足需要保存很多状态的迭代器。这个时候有两个解决方案:closure和table。closure的使用与第一节讲的那样(values例子的closure只保存了i但可以保存更多);而table是指用一个table来保存多个状态。这个table可以修改其中的内容,即更新它保存的状态值,来达到控制遍历(结束条件)。

--table保存多个状态值风格的迭代器

function allwords()

?????? local state={line=io.read(),pos=1}

?????? return iterator,state

end

functioniterator(state)

?????? while state.line do

????????????? local s,e=string.find(state.line,"%w+",state.pos)

????????????? if s then

???????????????????? state.pos=e+1

???????????????????? returnstring.sub(state.line,s,e)

????????????? else

???????????????????? state.line=io.read()

???????????????????? state.pos=1

????????????? end

?????? end

?????? return nil

end

io.input("./words.txt")

for word inallwords() do

?????? print(word)

end

虽然state是定义在allwords()函数中的局部变量,但它return了这个局部变量赋给泛型for的_s变量(在调用allwords()时),因此也不存在closure。但是closure比用table更加高效。因为创建一个closure比创建一个table更廉价,并且访问“非局部的变量”比访问table字段更快。

5、“真正”的迭代器

上述的迭代器其实是一个工厂函数,更准确的叫法应该是“生成器(generator)”。本节的“‘真正’的迭代器”是指在一个函数中完成迭代操作,而不是返回一个迭代器函数。也就不需要再用循环语句去调用返回的迭代器函数。这种迭代器接受一个函数作为参数,并在其内部的循环中调用这个函数

functionallwords(f) --函数作为参数

?????? for line in io.lines() do

????????????? for word instring.gmatch(line,"%w+") do

???????????????????? f(word) --在循环中调用函数

????????????? end

?????? end

end

allwords(print)--调用迭代器。单纯的打印单词。

也可以使用匿名函数作为参数。

“真正的迭代器”是老版本中的用法,因为那时没有for语句。两者都有相同的开销,但“生成器”更具灵活性——它允许两个或多个并行的迭代过程(例如要逐字比较两个文件,那就需要同时遍历两个文件),其次它允许在迭代中使用break和return。而在真正的迭代器风格写法中 return 语句只是从匿名函数中返回而不是退出循环。

6、小结

本章是介绍如何设计迭代器和泛型for的原理。迭代器实现主要有

方式

说明

有无closure

closure

?

泛型for

泛型for的语义

table

复杂状态的迭代器用table来保存

“真正”的迭代器

迭代器中完成迭代操作

(编辑:李大同)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

    推荐文章
      热点阅读