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


7.4 – 具有复杂状态的迭代器

通常,迭代器需要保留比单个不变状态和控制变量更多的状态。最简单的解决方案是使用闭包。另一种解决方案是将所有需要的内容打包到一个表中,并使用此表作为迭代的不变状态。使用表,迭代器可以在循环中保留所需的数据。此外,它可以在进行时更改该数据。虽然状态始终是同一张表(因此是不变的),但表内容会在循环中发生变化。由于此类迭代器将所有数据都保存在状态中,因此它们通常会丢弃通用 for 提供的第二个参数(迭代器变量)。

作为此技术的示例,我们将重写迭代器 allwords,它遍历当前输入文件中的所有单词。这一次,我们将使用具有两个字段(linepos)的表来保留其状态。

启动迭代的函数很简单。它必须返回迭代器函数和初始状态

    local iterator   -- to be defined later
    
    function allwords ()
      local state = {line = io.read(), pos = 1}
      return iterator, state
    end
iterator 函数执行实际工作
    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


只要有可能,你都应该尝试编写无状态迭代器,即那些将所有状态保存在 for 变量中的迭代器。使用它们时,你不会在开始循环时创建新对象。如果你无法将你的迭代放入该模型中,那么你应该尝试闭包。除了更优雅之外,闭包通常比使用表的迭代器更有效率:首先,创建闭包比创建表更便宜;其次,访问上值比访问表字段更快。稍后我们将看到另一种编写迭代器的方法,即协程。这是最强大的解决方案,但成本稍高。