新的 For 循环

lua-users home
wiki

摘要: 本文讨论了 Lua 5.0 中引入的扩展迭代器风格的 'for' 语句语法。此页面有些历史意义(并且不再是新的)。参考手册[1] 对此进行了更完整的描述。

介绍

我(PhilippeLhoste)在 Lua 5.0 中使用新的 'for' 语法玩了一点(work0 以防语法/行为发生变化)。

我从 Roberto 在 2002/06/06 的邮件开始,邮件中给出了伪代码。由于我更喜欢长的变量名,所以我对其进行了重写,以便更好地(对我而言!)理解。让我在这里引用它,作为提醒

伪代码

for var1, ..., varn in expList do
  block
end

等效于以下[1][2](Lua 5.1)

do
  local _func, _state, var = <explist>
  while 1 do
    local var1, ..., varn = _func(_state, var1)
    var = var1
    if var == nil then break end
    <block>
  end
end

explist 是两个或三个表达式:一个函数 (_func)、一个状态(用于数据的持久性)和一个初始值。它可以是一个返回这些值的函数。

_funcexplist 返回,必须返回 n 个值,第一个值为 nil 时表示进程已耗尽。

我尝试了以下代码(也是 Roberto 提供的)

t = { "a", "b", "c" }
for i, k in nexti(t) do print(i, k) end
for k, v in next, t do print(k, v) end
for k, v in t do print(k, v) end

结果相同。

在第一行中,'nexti' 返回函数迭代器和作为参数给出的表。

在第二行中,'next' 是函数 (_func),而 't' 是状态 (_state)。

第三行是上述行的语法糖,以确保向后兼容性。

应用:读取文件的行

Roberto 给出的用于读取文件行的代码片段帮助我理解了上面的伪代码(我有点慢...)。

它是

function lines(filename)
  local f = assert(io.open(filename, "r"))
  return function ()
    return f:read() or (assert(f:close()) and nil)
  end
end

for line in lines(file) do
  print(line) -- Or process line, etc.
end

对于那些想知道匿名函数返回值的人

- 如果 f:read() 读取一行,则 'or' 之后的代码不会被执行。

- 否则(行尾)f:read()nil,我们返回第二部分

如果断言成功,则返回其表达式。

在任何情况下,"and nil" 都强制此部分为 nil,因此循环结束。

使用 'for' 语法玩耍

我开始像这样重写它

function lines(filename)
  local f = assert(io.open(filename, "r"))
  local i = 0
  return function ()
    i = i + 1
    return f:read() or (assert(f:close()) and nil), i
  end
end

local line, number
for line, number in lines(file) do
  print("(" .. number .. ") " .. line)
end

我们已经有了状态持久性... 我想这就是 5.0 闭包的魔力,匿名函数中的 'i' 指向 lines() 中的局部变量。

我制作了一个最终版本,使用状态和初始值,如伪代码所示

function lines(filename)
  local f = assert(io.open(filename, "r"))
  local state = {}
  state.persistentValue = " "
  state.counter = 0
  return function (_state, _previousValue)
    _state.persistentValue = "." .. _state.persistentValue
    _state.counter = _state.counter + 1
    print(_state.persistentValue .. _previousValue)
    return f:read() or (assert(f:close()) and nil), _state.counter
  end, state, "First value"
end

但我不再确定这种状态相对于先前版本的优势... 好吧,它看起来更像 OO,我可能遗漏了一些副作用。

RobertoIerusalimschy 回答道:这取决于个人喜好。使用状态的优势在于,在运行 for 循环时,不需要创建任何新的“对象”(表、闭包等)。`nexti` 就是一个例子。在“更重”的循环中,额外对象的成本可以忽略不计。例如,在文件示例中,您已经需要打开文件、创建文件句柄、创建多个字符串(行)等等。一个额外的闭包(或表)对总成本的影响微乎其微。

另请参阅


最近更改 · 偏好设置
编辑 · 历史记录
最后编辑于 2008 年 3 月 29 日下午 9:58 GMT (差异)