Lua之迭代器和范性for
基本环境:Microsoft Windows XP/Service Pack 2Lua 5.1.4 Copyright (C) 1994-2008 Lua.org, PUC-Rio迭代器:百度百科的解释:迭代器(iterator)是一种对象,它能够用来遍历标准模板库容器中的部分或全部元素,每个迭代器对象代表容器中的确定的地址。而在Lua 中我们常常使用函数来描述迭
基本环境:
Microsoft Windows XP/Service Pack 2
Lua 5.1.4 Copyright (C) 1994-2008 Lua.org, PUC-Rio
迭代器:
百度百科的解释:迭代器(iterator)是一种对象,它能够用来遍历标准模板库容器中的部分或全部元素,每个迭代器对象代表容器中的确定的地址。
而在Lua 中我们常常使用函数来描述迭代器,每次调用该函数就返回集合的下一个元素。
先看一个例子:
function list_iter(t)
local i = 0
local n = table.getn(t)
return function ()
i = i + 1
if i <= n then return t[i] end
end
end
按照上面的文字定义,我们可以很清楚的知道list_iter返回的匿名函数就是Lua中的迭代器。
t = {10, 20, 30}
iter = list_iter(t)
while true do
local element = iter()
if element == nil then break end
print (element)
end
上面的iter就是我们所需要的迭代器。在while循环中,调用iter(),我们就可以依次取得t中的元素。
这里正是利用了闭包的特点,,每次调用list_iter都会创建一个新的闭包(迭代器本身)。闭包保存内部局部变量(t,i,n),因此每次调用他返回list 中的下一个元素值,当list 中
没有值时,返回nil.
范性for:
先看一段代码
for element in list_iter(t) do
print(element)
end
在不往下看之前,我们可以先试图根据我们已有的知识结构去理解这段代码。如果这样,list_iter(t)应该返回一个类似集合的东西,而我们已经知道实际上只返回了一个匿名函
数,也就是迭代器。当然,每次调用迭代器都可以得到一个元素,迭代器的所有的结果倒是可以看成一个集合。因素齐了,我们需要一个逻辑上的解释,这个逻辑就是范性for
的语义。
先看文法规定:
for <var-list> in <exp-list> do
<body>
end
整个过程是这样的:
首先,初始化,计算 in 后面表达式的值,表达式应该返回范性for 需要的三个值:迭代函数、状态常量、控制变量;与多值赋值一样,如果表达式返回的结果个数不足三个会自
动用nil 补足,多出部分会被忽略。
第二,将状态常量和控制变量作为参数调用迭代函数(注意:对于 for 结构来说,状态常量没有用处,仅仅在初始化时获取他的值并传递给迭代函数)。
第三,将迭代函数返回的值赋给变量列表。
第四,如果返回的第一个值为 nil 循环结束,否则执行循环体。
第五,回到第二步再次调用迭代函数。
更具体地说:
for var_1, ..., var_n in explist do block end
等价于
do
local _f, _s, _var = explist
while true do
local var_1, ... , var_n = _f(_s, _var)
_var = var_1
if _var == nil then break end
block
end
end
范性 for 在自己内部保存三个值:迭代函数、状态常量、控制变量。
闭包、迭代器和范性for:
到现在,Lua为我们准备了三块积木:闭包、范性for和迭代器。一个循环,我们可以利用闭包+迭代器,也可以使用范性for+迭代器。那我们该怎么取舍呢?Lua也给出了建
议。
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
for i, v in ipairs(a) do
print(i, v)
end
这种情况是Lua最推荐的,迭代器不依赖upvalue,不产生闭包,状态常量和控制变量借助范性for保存,通过迭代器的参数传递给了迭代器。
再给一个书中的例子:
local iterator -- to be defined later
function allwords()
local state = {line = io.read(), pos = 1}
return iterator, state
end
function iterator (state)
while state.line do -- repeat while there are lines
-- search for next word
local s, e = string.find(state.line, "%w+", state.pos)
if s then -- found a word?
-- update next position (after this word)
state.pos = e + 1
return string.sub(state.line, s, e)
else -- word not found
state.line = io.read() -- try next line...
state.pos = 1 -- ... from first position
end
end
return nil -- no more lines: end loop
end
这样好不好呢,Lua给的答案是否定的。书中有一段话说得很清楚:
我们应该尽可能的写无状态的迭代器,因为这样循环的时候由for 来保存状态,不需要创建对象花费的代价小;如果不能用无状态的迭代器实现,应尽可能使用闭包;尽可能不
要使用table 这种方式,因为创建闭包的代价要比创建table 小,另外Lua 处理闭包要比处理table 速度快些。
参考:
1 programming in Programming in Lua
2 lua-5.1中文手册
更多推荐
所有评论(0)