第一版是为 Lua 5.0 编写的。虽然在很大程度上仍然适用于更高版本,但有一些差异。
第四版针对 Lua 5.3,可在 Amazon 和其他书店购买。
购买本书,您还可以帮助支持 Lua 项目


7.1 – 迭代器和闭包

迭代器是允许您迭代集合元素的任何构造。在 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 是工厂。每次我们调用它时,它都会创建一个新的闭包(迭代器本身)。该闭包在其外部变量(tin)中保留其状态,以便每次我们调用它时,它都会从列表 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 实际上做了更多的事情。)

作为一个更高级的示例,我们将编写一个迭代器来遍历当前输入文件中的所有单词。要进行此遍历,我们需要保留两个值:当前行以及我们在该行中的位置。有了这些数据,我们始终可以生成下一个单词。为了保留它,我们使用两个外部局部变量,linepos

    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 进行编程的最终用户不会定义迭代器,而只会使用应用程序提供的迭代器。