Skip to content

Lua 迭代器

迭代器(Iterator)是一种机制,允许你逐个遍历集合(在 Lua 中通常是 table)中的所有元素。Lua 的泛型 for 循环是使用迭代器的主要方式。

泛型 for 循环使用一个迭代器函数来遍历集合。其语法如下:

for var_1, var_2, ..., var_n in iterator_function, state, initial_value do
-- 循环体
end

Lua 提供了两个常用的内置 table 迭代器函数:

  • ipairs(t): 遍历 table t 的整数键,从 1 开始直到第一个缺失的整数键为止(即用于类数组的序列)。它在每一步返回索引(键)和对应的值。
  • pairs(t): 遍历 table t 中的所有键值对,包括非整数键和非连续的整数键。pairs 的遍历顺序是不确定的。

使用 ipairs 的示例(用于序列):

local days = {"Monday", "Tuesday", "Wednesday", "Thursday", "Friday"}
for index, dayName in ipairs(days) do
print(index, dayName)
end
-- 输出:
-- 1 Monday
-- 2 Tuesday
-- 3 Wednesday
-- 4 Thursday
-- 5 Friday

使用 pairs 的示例(用于通用 table):

local person = { name = "Alice", age = 30, city = "New York" }
person[1] = "Extra Info" -- 添加一个数字键
for key, value in pairs(person) do
print(key, value)
end
-- 可能的输出(顺序可能不同):
-- name Alice
-- city New York
-- 1 Extra Info
-- age 30

在 Lua 中,迭代器是通过函数实现的。泛型 for 循环会重复调用这个迭代器函数。根据状态维护方式,自定义迭代器主要有两种类型:

  • 无状态迭代器
  • 有状态迭代器

无状态迭代器是一个函数,在每次迭代时,它从 for 循环本身接收当前的状态和控制变量。它不需要在函数调用之间内部保存任何状态;for 循环负责管理状态。

我们来创建一个无状态迭代器,它生成直到某个限制的数字的平方。

-- 迭代器函数:square_iter(limit, current_num)
-- limit: 不变状态(最大计数)
-- current_num: 控制变量
function square_iter(limit, current_num)
current_num = current_num + 1
if current_num <= limit then
return current_num, current_num * current_num -- 返回新的控制变量,值
end
-- 没有更多值了,返回 nil 停止迭代
end
-- 直接将无状态迭代器函数与 for 循环一起使用
-- for <循环变量> in <迭代器函数>, <不变状态>, <初始控制变量> do
print("使用直接迭代器函数生成平方:")
for i, n_squared in square_iter, 3, 0 do
print(i, n_squared)
end
-- 输出:
-- 1 1
-- 2 4
-- 3 9

通常,你会将迭代器函数包装在另一个函数(一个工厂函数,factory)中,这个工厂函数为 for 循环设置初始状态,类似于 ipairs 和 pairs 的工作方式。

-- 平方迭代器的工厂函数
function squares_factory(limit)
-- 返回:迭代器函数,不变状态,初始控制变量值
return square_iter, limit, 0
end
print("\n使用工厂函数生成平方:")
for i, n_squared in squares_factory(4) do
print(i, n_squared)
end
-- 输出:
-- 1 1
-- 2 4
-- 3 9
-- 4 16

有状态迭代器使用闭包(closure)在函数调用之间维护其状态。闭包是一个捕获(关闭)其周围局部变量(upvalue)的函数。要创建有状态迭代器,通常会定义一个工厂函数来创建并返回该闭包。

我们来使用闭包创建一个用于数组元素的迭代器。

local fruits = {"Apple", "Banana", "Cherry"}
-- 返回迭代器(一个闭包)的工厂函数
function element_iterator_factory(collection)
local index = 0 -- 闭包变量(Upvalue),迭代器的状态
local count = #collection -- 闭包变量(Upvalue),另一部分状态
-- 这是闭包,实际的迭代器函数
return function()
index = index + 1
if index <= count then
return collection[index] -- 返回下一个元素
else
return nil -- 没有更多元素了,停止迭代
end
end
end
print("\n使用有状态迭代器遍历水果:")
-- for 循环调用 element_iterator_factory 返回的闭包
for fruit in element_iterator_factory(fruits) do
print(fruit)
end
-- 输出:
-- Apple
-- Banana
-- Cherry

在这个例子中,element_iterator_factory 被调用一次。它设置了 index 和 count。内部的匿名函数(即闭包)被返回并成为迭代器。每次 for 循环调用这个闭包时,它可以访问并修改其封闭作用域中的 index 闭包变量(upvalue),从而维护其状态。

实际应用: 迭代器在处理集合时无处不在。自定义迭代器对于抽象复杂数据结构或序列(如从文件读取行(io.lines)、遍历树或按需生成值)非常强大。