A:一种允许你访问一个集合中的元素的构造。在Lua中用函数来表示”iterator”,每次调用这个函数,函数就返回集合中的下一个元素。
A:使用”Closure”就可以创建”iterator”。
-- 一个类似于ipairs()的iterator,但他只返回table中的value。
function list_iter(t)
local i = 0
return function()
i = i + 1
if i <= #t then return t[i] end
end
end
t = {10, 20, 30}
-- 用"while"使用这个"iterator"。
iter = list_iter(t) -- creates the iterator
while true do
local element = iter() -- calls the iterator
if element == nil then break end
print(element)
end
-- 用"Generic for"使用这个"iterator"。
for element in list_iter(t) do
print(element)
end
可以看出,”iterator”与”Generic for”配合的更好。省去了iter
变量保存这个”iterator”,省去了判断break
条件,省去了显式调用”iterator”。实际上”Generic for”还做了更多的事情,在之后的”Q & A”总会看到如何创建一个高效的”iterator”,其中就用到了”Generic for”额外的特性。
A:之前有提到过“Generic for”的语法,
for var1, var2, ..., var_n in exp do
dosomething
end
实际上其内部做了如下的工作,
do
--[[ 首先计算"in"后面的"exp"。 实际上"Generic for"允许"exp"返回三个值,他们的含义分别是: _f: "iterator function", _s: "invariant state", _var: "control variable"。]]
local _f, _s, _var = exp
while true do
--[[ 以"invariant state"和"control variable"作为参数调用"iterator function"。 如果"iterator"还有值,则"var_1"到"var_N"都会得到值, 否则"iterator"返回"nil",即"var_1"到"var_N"都会得到"nil"]]
local var_1, ... , var_n = _f(_s, _var)
_var = var_1 -- "control variable"得到"var_1"的值。
--[[ 如果"control variable"得到的是"nil", 说明"iterator"中已经没有值了,那么跳出"for"循环。]]
if _var == nil then break end
--[[ 否则"control variable"得到的不是"nil", 说明"iterator"中还有值,那么执行"for"中给出的代码。]]
dosomething -- 这个"dosomething"是上面"Generic for"循环中的那个"dosomething"。
end
end
结合一个实际的例子来理解会更容易一些,
-- 用Lua实现ipairs()。
function iter(a, i) -- 每次都会传递"invariant state"和"control variable"。
i = i + 1 -- 更新"control variable"。
local v = a[i]
if v then
--[[ 首先返回新的"control variable" (因为"Generic for"的内部实现中"control variable"接收的是"var_1"的值), 再返回其他的值。]]
return i, v
end
end
function ipairs(a)
--[[ iter是"iterator function", a是"invariant state", 0是"control variable"的初始值。 结合上面"Generic for"的内部实现,就能明白这3个变量的意义了。]]
return iter, a, 0
end
A:不需要自己保存”iterator”的状态以及”control variable”,所有均由”Generic for”负责管理。
A:需要自己保存”iterator”的状态以及,并且在调用”iterator”的过程中其状态会变化。一般会使用一个”table”来保存会变化的状态(同时,一般也将”iterator”的”control variable”放入其中,所以此种模式的”iterator”一般不需要”Generic for”的”control variable”),然后将此”table”作为”invariant state”。
A:首先应该尝试写一个”stateless iterator”,因为”Generic for”内部会帮助你管理”iterator”的状态,每一次调用”iterator”也没有额外的开销。如果这种模式不合适,那么就要尝试写一个”closure”,”closure”的开销比”table”的开销小,同时”closure”的访问速度也比”table”的访问速度快。如果这种模式依旧不合适,你才要考虑写一个”complex state iterator”。最后如果实在没办法了,你可以使用”coroutine”(在此不提,之后会提到)来创建”iterator”,他的功能十分强大,但是开销也不小。
“stateless iterator”的典型例子是上面用Lua实现的ipairs()。
接下来,通过一个找出输入的字符串中每一个单词的例子,展示如何创建”closure”模式以及”complex state iterator”模式的”iterator”。
--[[ "closure" mode.
每次使用时都会创建一个"closure"(自己保存"line"变量和"pos"变量)。]]
function allwords ()
local line = io.read() -- current line
local pos = 1 -- current position in the line
return function () -- iterator function
while line do -- repeat while there are lines
local s, e = string.find(line, "%w+", pos)
if s then -- found a word?
pos = e + 1 -- next position is after this word
return string.sub(line, s, e) -- return the word
else
line = io.read() -- word not found; try next line
pos = 1 -- restart from first position
end
end
return nil -- no more lines: end of traversal
end
end
-- 用"Generic for"使用这个"iterator"。
for word in allwords() do
print(word)
end
--[[ "complex state iterator" mode.
"line"变量和"pos"变量存储在"state"这张"table"中,
"state"作为"Generic for"的"invariant state"。]]
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
function allwords ()
local state = {line = io.read(), pos = 1}
return iterator, state
end
-- 用"Generic for"使用这个"iterator"。
for word in allwords() do
print(word)
end
1、在连续调用”iterator”的过程中,”iterator”需要保存一个“状态”,通过这个“状态””iterator”才能知道如何获取下一个元素,“Closures”能很好的完成这个任务(“Closures”是一个能够访问包含他的函数中的局部变量的函数)。