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


7.2 – 通用 for 的语义

这些先前迭代器的一个缺点是,我们需要为每个新循环创建一个新闭包。对于大多数情况而言,这不是一个真正的问题。例如,在 allwords 迭代器中,与读取整个文件相比,创建一个单个闭包的成本可以忽略不计。但是,在少数情况下,这种开销可能是不可取的。在这种情况下,我们可以使用通用 for 本身来保持迭代状态。

我们看到,通用 for 在循环期间在内部保留迭代器函数。实际上,它保留三个值:迭代器函数、不变状态控制变量。现在让我们看看详细信息。

通用 for 的语法如下

    for <var-list> in <exp-list> do
      <body>
    end
其中 <var-list> 是一个或多个变量名称的列表,用逗号分隔,<exp-list> 是一个或多个表达式的列表,也用逗号分隔。通常,表达式列表只有一个元素,即对迭代器工厂的调用。例如,在代码中
    for k, v in pairs(t) do
      print(k, v)
    end
变量列表是 k, v;表达式列表有一个元素 pairs(t)。通常,变量列表也只有一个变量,如
    for line in io.lines() do
      io.write(line, '\n')
    end
我们称列表中的第一个变量为控制变量。在循环期间,它的值永远不会为 nil,因为当它变为 nil 时,循环结束。

for 做的第一件事是计算 in 之后的表达式。这些表达式应生成 for 保留的三个值:迭代器函数、不变状态和控制变量的初始值。就像在多重赋值中,列表的最后一个(或唯一一个)元素才能生成多个值;并且值的数量调整为三个,根据需要丢弃多余的值或添加 nil。(当我们使用简单的迭代器时,工厂仅返回迭代器函数,因此不变状态和控制变量获得 nil。)

在初始化步骤之后,for 使用两个参数调用迭代器函数:不变状态和控制变量。(请注意,对于 for 结构,不变状态没有任何意义。它仅从初始化步骤获取此值,并在调用迭代器函数时传递此值。)然后,for 将迭代器函数返回的值分配给其变量列表声明的变量。如果返回的第一个值(分配给控制变量的值)为 nil,则循环终止。否则,for 将执行其主体并再次调用迭代函数,重复该过程。

更准确地说,类似这样的构造

    for var_1, ..., var_n in explist do block end
等效于以下代码
    do
      local _f, _s, _var = explist
      while true do
        local var_1, ... , var_n = _f(_s, _var)
        _var = var_1
        if _var == nil then break end
        block
      end
    end
因此,如果我们的迭代器函数为 f,不变状态为 s,控制变量的初始值为 a0,则控制变量将循环遍历值 a1 = f(s, a0)a2 = f(s, a1) 等,直到 ainil。如果 for 有其他变量,它们只需获取 f 每次调用返回的多余值即可。