第 8 章 迭代器
第 8 章 迭代器
迭代器把“逐个产生值”的细节封装起来。Lua 使用泛型 for 驱动迭代器,语言内置的 pairs 和 ipairs 已经覆盖了大多数表遍历场景。
泛型 for 的机制
泛型 for 接收三个值:迭代函数、状态和初始控制值。每轮调用迭代函数,直到第一个返回值为 nil:
local function iterator(state, control)
local nextValue = control + 1
if nextValue <= state.limit then
return nextValue, nextValue * nextValue
end
end
for value, square in iterator, { limit = 4 }, 0 do
print(value, square)
end
实际开发中通常直接使用库函数或自定义闭包迭代器,不需要手动构造三元组。
pairs 与 ipairs
pairs 遍历全部键值对,顺序未定义:
local data = { a = 1, b = 2, c = 3 }
for key, value in pairs(data) do
print(key, value)
end
ipairs 从键 1 开始,遇到第一个 nil 停止:
local values = { "a", "b", "c" }
for index, value in ipairs(values) do
print(index, value)
end
判断“是否存在某键”时应使用 nil 比较,不要依赖真假转换:
if data.a ~= nil then
print("存在 a")
end
自定义迭代器
闭包可以保存遍历状态:
local function range(first, last, step)
local current = first - step
return function()
current = current + step
if current <= last then
return current
end
end
end
for value in range(1, 5, 1) do
print(value)
end
遍历树或链式结构时也可以使用闭包:
local function stackItems(root)
local stack = { root }
return function()
local node = table.remove(stack)
if not node then
return nil
end
for index = #node.children, 1, -1 do
stack[#stack + 1] = node.children[index]
end
return node.value
end
end
无状态迭代器
无状态迭代器只依赖状态和控制变量,不捕获可变外部状态。它更容易复用,也便于推理:
local function digits(text)
return function(source, position)
local startIndex, endIndex = source:find("%d+", position)
if not startIndex then
return nil
end
return tonumber(source:sub(startIndex, endIndex)), endIndex + 1
end, text, 1
end
for number in digits("a12b345c") do
print(number)
end
当迭代器需要读取文件、网络或复杂对象时,有状态闭包通常更直观;当迭代完全由控制变量决定时,无状态迭代器更合适。
使用 coroutine.wrap 简化生成器
local function linesFromText(text)
return coroutine.wrap(function()
for line in text:gmatch("[^\n]+") do
coroutine.yield(line)
end
end)
end
for line in linesFromText("a\nb\nc") do
print(line)
end
协程的详细用法会在第 13 章展开。这里只需要知道它可以把复杂生成逻辑写成顺序代码。