第一版是为 Lua 5.0 编写的。虽然在很大程度上仍然适用于更高版本,但有一些差异。
第四版针对 Lua 5.3,可在 Amazon 和其他书店购买。
购买本书,您还可以帮助支持 Lua 项目。
![]() |
用 Lua 编程 | ![]() |
| 第一部分 语言 第 7 章 迭代器和通用 for |
迭代器是允许您迭代集合元素的任何构造。在 Lua 中,我们通常用函数表示迭代器:每次调用该函数,它都会从集合中返回一个“下一个”元素。
任何迭代器都需要在连续调用之间保持一些状态,以便它知道它在哪里以及如何从那里继续。闭包为该任务提供了一种出色的机制。请记住,闭包是一个函数,它访问其封闭函数中的一个或多个局部变量。这些变量在对闭包的连续调用中保持其值,使闭包能够记住它在遍历中的位置。当然,要创建新的闭包,我们还必须创建其外部局部变量。因此,闭包构造通常涉及两个函数:闭包本身;和工厂,创建闭包的函数。
作为一个简单的示例,让我们为列表编写一个简单的迭代器。与 ipairs 不同,此迭代器不返回每个元素的索引,只返回该值
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 是工厂。每次我们调用它时,它都会创建一个新的闭包(迭代器本身)。该闭包在其外部变量(t、i 和 n)中保留其状态,以便每次我们调用它时,它都会从列表 t 中返回一个下一个值。当列表中没有更多值时,迭代器返回 nil。
我们可以使用这样的迭代器和 while
t = {10, 20, 30}
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
但是,使用通用 for 更容易。毕竟,它就是为这种迭代设计的
t = {10, 20, 30}
for element in list_iter(t) do
print(element)
end
通用 for 会处理迭代循环中的所有簿记:它调用迭代器工厂;在内部保留迭代器函数,因此我们不需要 iter 变量;在每次新迭代时调用迭代器;当迭代器返回 nil 时停止循环。(稍后我们将看到通用 for 实际上做了更多的事情。)
作为一个更高级的示例,我们将编写一个迭代器来遍历当前输入文件中的所有单词。要进行此遍历,我们需要保留两个值:当前行以及我们在该行中的位置。有了这些数据,我们始终可以生成下一个单词。为了保留它,我们使用两个外部局部变量,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
迭代器函数的主要部分是调用 string.find。此调用从当前位置开始在当前行中搜索单词。它使用模式 '%w+' 描述“单词”,该模式匹配一个或多个字母数字字符。如果找到单词,该函数会将当前位置更新为单词后的第一个字符,并返回该单词。(string.sub 调用从 line 中提取给定位置之间的子字符串)。否则,迭代器会读取新行并重复搜索。如果没有更多行,它会返回 nil 以表示迭代结束。
尽管 allwords 很复杂,但其用法却很简单
for word in allwords() do
print(word)
end
这是迭代器的常见情况:它们可能很难编写,但很容易使用。这不是一个大问题;通常,使用 Lua 进行编程的最终用户不会定义迭代器,而只会使用应用程序提供的迭代器。
| 版权所有 © 2003–2004 Roberto Ierusalimschy。保留所有权利。 | ![]() |