新的 for 语句 |
|
for' 语句语法。本页面在某种程度上是历史性的(已经不再是新的了)。参考手册 [1] 对此有更完整的描述。
我(PhilippeLhoste)尝试了 Lua 5.0 中的新 'for' 语法(工作在 5.0 版本,因为语法/行为可能会改变)。
我从 Roberto 在 2002/06/06 的邮件开始,其中提供了伪代码。由于我喜欢长变量名,我重写了一下以便(我个人认为)更好地理解。在此引用,作为提醒:
伪代码
for var1, ..., varn in expList do block end
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,我们会返回第二部分。
and nil" 会强制这一部分为 nil,因此循环结束。
我开始将其重写为:
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 是一个例子。在“更重”的循环中,额外对象的成本可以忽略不计。例如,在文件示例中,您已经需要打开文件、创建文件句柄、创建几个字符串(行)等等。一个额外的闭包(或表)对总成本影响很小。