Lua之迭代器和范性for

来源:互联网 发布:珍妮曲奇授权的淘宝店 编辑:程序博客网 时间:2024/04/30 13:09

基本环境:

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 = 0local n = table.getn(t)return function ()i = i + 1if i <= n then return t[i] endendend
按照上面的文字定义,我们可以很清楚的知道list_iter返回的匿名函数就是Lua中的迭代器。
t = {10, 20, 30}iter = list_iter(t)while true dolocal element = iter()if element == nil then break endprint (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

等价于

dolocal _f, _s, _var = explistwhile true dolocal var_1, ... , var_n = _f(_s, _var)_var = var_1if _var == nil then break endblockendend

范性 for 在自己内部保存三个值:迭代函数、状态常量、控制变量。


闭包、迭代器和范性for:

到现在,Lua为我们准备了三块积木:闭包、范性for和迭代器。一个循环,我们可以利用闭包+迭代器,也可以使用范性for+迭代器。那我们该怎么取舍呢?Lua也给出了建

议。

function iter (a, i)i = i + 1local v = a[i]if v thenreturn i, vendendfunction ipairs (a)return iter, a, 0endfor i, v in ipairs(a) doprint(i, v)end
这种情况是Lua最推荐的,迭代器不依赖upvalue,不产生闭包,状态常量和控制变量借助范性for保存,通过迭代器的参数传递给了迭代器。

再给一个书中的例子:

local iterator -- to be defined laterfunction allwords()local state = {line = io.read(), pos = 1}return iterator, stateendfunction iterator (state)while state.line do -- repeat while there are lines-- search for next wordlocal s, e = string.find(state.line, "%w+", state.pos)if s then -- found a word?-- update next position (after this word)state.pos = e + 1return string.sub(state.line, s, e)else -- word not foundstate.line = io.read() -- try next line...state.pos = 1 -- ... from first positionendendreturn nil -- no more lines: end loopend

这样好不好呢,Lua给的答案是否定的。书中有一段话说得很清楚:

我们应该尽可能的写无状态的迭代器,因为这样循环的时候由for 来保存状态,不需要创建对象花费的代价小;如果不能用无状态的迭代器实现,应尽可能使用闭包;尽可能不

要使用table 这种方式,因为创建闭包的代价要比创建table 小,另外Lua 处理闭包要比处理table 速度快些。


参考:

1 programming in Programming in Lua

2 lua-5.1中文手册