Lua程序设计第二版(笔记) 第七章迭代器与泛型for

来源:互联网 发布:淘宝店铺怎么申请花呗 编辑:程序博客网 时间:2024/06/06 10:49

迭代器与closure

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

一个closure结构通常涉及到两个函数:closure本身和一个用于创建该closure的工厂函数。

例如:

function values(t)

local i = 0

return function() i = i + 1;

return t[i] end

end

values就是一个工厂。每当调用这个工厂时,它就创建一个新的closure。这个closure将它的状态保存在其外部变量t 和 i中。每当调用这个迭代器时,它就从列表中返回下一个值。直到最后一个元素返回后,迭代器就会返回nil。表示结束。

可以在一个while循环中使用这个迭代器:

t = { 10 ,20 ,30 }

iter = values(t)

while true do

locale element = iter()

if element == nil  then

break end

else

print( element )

end

使用泛型for:

t = { 10, 20, 30 }

for element in values(t) do

print(element)

end

 泛型for为一次迭代循环做了所有的簿记工作。它在内部保存了迭代器函数,因此不需要iter变量。它在每次新迭代时调用迭代器,并在迭代器返回nil时结束循环。


泛型for的语义

泛型for在循环过程内部保存了迭代器函数。实际上它保存着3个值:一个迭代器函数、一个恒定状态和一个控制变量。

泛型for的语法如下:

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

<body>

end

<var-list>是一个或多个变量名的列表,以逗号分隔;<exp-list>是一个或多个表达式的列表,同样以逗号分隔。

for k , v in pairs(t) do 

print(k, v)

end

其中变量列表是"k,v",表达式列表只有一个元素pairs(t)。一般来说变量列表中也只有一个变量,例如下面这个循环:

for line in io.lines() do

io.write(line, "\n")

end

变量列表的第一个元素称为“控制变量”。在循环过程中该值绝不会为nil,因为当它为nil时循环就结束了。


无状态的迭代器

所谓“无状态的迭代器”,就是一种自身不保存任何状态的迭代器。我们可以在多个循环中使用同一个无状态的迭代器,避免创建新的closure开销。

每次迭代中,for循环都会用恒定状态和控制变量来调用迭代器函数。一个无状态的迭代器可以根据这个值来为下次迭代生成下一个元素。

例如:

a  = { "one", "two", "three" }

for i, v in ipairs(a) do

pint( i , v )

end

迭代的状态就是遍历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

当Lua调用for循环中的ipairs(a)时,它会获得3个值:迭代器函数iter、恒定状态a和控制变量的初值0。然后Lua调用iter(a, 0),得到1,a[1]。在第二个迭代中,继续调用iter(a, 1),得到2,a[2],依次类推,直至得到第一nil元素为止。

函数pairs与ipairs类似,也是用于遍历一个table中的所有元素。不同的是,它的迭代器函数是Lua中的一个基本函数next。

function pairs(t)

return next, t, nil

end

调用next(t,k)时,k是table t的一个key。此调用会以table中的任意次序返回一组值:此talbe的下一个key,及这个key 所对应的值。而调用next(t, nil)时,返回table的第一组值。若没有下一组值时,next返回nil。

for k , v in next, t do

<loop body>

end

Lua会自动将for循环中表达式列表的结果调整为3个值。因此上例中得到了next、t和nil,这也正与调用的pairs(t)的结果完全一致。

遍历链表的迭代器:

local function getnext( list, node )

if not node then

return list

else

return node.next

end

edn

function traverse(list)

return getnext, list, nil

end

这个列子是将链表的头结点作为恒定状态,而将当前结点作为控制变量。第一次调用迭代器函数getnext时,node为nil,因此函数返回list作为第一个结点。

list = nil

for line in io.lines() do

list = { val = line, next = list }

end

for node  in traverse( list ) do

print( node.val )

end