第一版是为 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。保留所有权利。 |