“迭代器”这个词多少有点误导性,这是因为迭代器并没有进行实际的迭代:真正的迭代是 for
循环完成的,迭代器只不过为每次的迭代提供连续的值。或许,称其为“生成器”更好,表示为迭代生成元素;不过,“迭代器”这个名字已经在诸如 Java
等其他语言中被广泛使用了。
然而,还有一种创建迭代器的方式可以让迭代器进行实际的迭代操作。当使用这种迭代器时,就不需要编写循环了。相反,只需要调用这个迭代器,并传入一个描述了在每次迭代时迭代器需要做什么的参数即可。更准确的说,迭代器接收了一个函数作为参数,这个函数在循环的内部被调用,这种迭代器就被称为真正的迭代器。
如下所示:
function allwords(f) for line in io.lines() do for word in string.gmatch(line, "%w+") do f(word) end end end
使用这个迭代器时,我们必须传入一个函数作为循环体。如果我们只想输出每个单词,那么简单地使用函数 print
即可:
allwords(print)
通常,我们可以使用一个匿名函数作为循环体。例如,以下的代码用于计算单词"hello"在输入文本中出现的次数:
local count = 0 allwords(function (w) if w == "hello" then count = count + 1 end end) print(count)
同样的需求,如果采用之前的迭代器风格,差异也不是特别大:
local count = 0 for w in allwords() do if w == "hello" then count = count + 1 end end print(count)
真正的迭代器在老版本的 Lua
语言中曾经非常流行,那是还没有 for
语句。
真正的迭代器与生成器风格的迭代器想比怎么样呢?这两种风格都有大致相同的开销,即每次迭代都有一次函数调用。一方面,编写真正的迭代器比较容易。另一方面,生成器风格的迭代器则更加灵活。首先,生成器风格的迭代器允许两个或更多个并行的迭代。其次,生成器风格的迭代器允许在循环体重使用 break
和 return
语句。使用真正的迭代器, return
语句从匿名函数中返回而并非从进行迭代的函数中返回。基于这些原因,高级 Lua
语言中一般更喜欢生成器风格的迭代器。