新的 for 语句

lua-users home
wiki

摘要: 本文讨论了 Lua 5.0 中引入的扩展的迭代器风格的 'for' 语句语法。本页面在某种程度上是历史性的(已经不再是新的了)。参考手册 [1] 对此有更完整的描述。

引言

我(PhilippeLhoste)尝试了 Lua 5.0 中的新 'for' 语法(工作在 5.0 版本,因为语法/行为可能会改变)。

我从 Roberto 在 2002/06/06 的邮件开始,其中提供了伪代码。由于我喜欢长变量名,我重写了一下以便(我个人认为)更好地理解。在此引用,作为提醒:

伪代码

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

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

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),一个状态(用于数据持久化)和一个初始值。它也可以是一个返回这些值的函数。

_func,由 explist 返回,必须返回 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,我们会返回第二部分。

如果 assert 的结果为真,它将返回其表达式。

无论如何,"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

但我不再确定这个状态相对于之前版本有什么优势了... 嗯,它看起来更像面向对象,而且我可能忽略了一些副作用。

RobertoIerusalimschy 回答:这是一个品味问题。使用状态的巨大优势在于,您无需创建任何新的“对象”(表、闭包等)来运行 for 循环。nexti 是一个例子。在“更重”的循环中,额外对象的成本可以忽略不计。例如,在文件示例中,您已经需要打开文件、创建文件句柄、创建几个字符串(行)等等。一个额外的闭包(或表)对总成本影响很小。

另请参阅


RecentChanges · preferences
编辑 · 历史
最后编辑于 2008 年 3 月 29 日 下午 4:58 GMT (差异)