迭代器与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