一、泛型 for 循环的解构
要理解迭代器,我们首先必须拆解泛型 for 循环的语法: for var_1, var_2, ..., var_n in <表达式列表> do ... end
这里的关键是 <表达式列表>。当循环开始时,Lua 会对这个列表求值一次,并期望它返回三个值:
- 迭代器函数(Iterator Function) :一个函数,
for循环在每次迭代时都会调用它来获取下一个(或下一组)值。 - 不变的状态(Invariant State):一个值(通常是表),它会在整个循环过程中被传入迭代器函数,用于维持状态。它本身通常不改变。
- 初始控制变量(Initial Control Variable):第一个要传入迭代器函数的值。
实际上,一个泛型 for 循环:
lua
for var_1, var_2 in explist do
-- 循环体
end
在 Lua 内部,等价于下面这段 while 循环代码:
lua
-- 1. 求值,获取三个关键部分
local _iterator, _state, _control_var = explist
-- 2. 循环开始
while true do
-- 3. 调用迭代器函数获取新值
local var_1, var_2 = _iterator(_state, _control_var)
-- 4. 更新控制变量为第一个返回值
_control_var = var_1
-- 5. 如果第一个返回值为 nil,则循环结束
if var_1 == nil then
break
end
-- 6. 执行循环体
-- 循环体
end
这就是 for 循环的全部秘密!它只是一种 while 循环的语法糖,遵循着这个"三值协议"。
二、迭代器的两种核心模式
理解了协议后,我们来看看两种最主要的迭代器实现模式。
1. 无状态迭代器(Stateless Iterator)
"无状态"指的是不变的状态 (_state),它通常就是我们正在遍历的那个对象(例如一个表)。下一次迭代所需的一切信息都包含在控制变量 (_control_var)中。
Lua 内置的 ipairs 和 pairs 就是典型的无状态迭代器。pairs(t) 实际上等价于 return next, t, nil。
- 迭代器函数 :
next,Lua 内置的用于遍历表的函数。 - 不变的状态 :
t,我们正在遍历的表。 - 初始控制变量 :
nil,告诉next函数从头开始。
让我们亲手实现一个 ipairs 来加深理解:
lua
local function my_ipairs_iterator(tbl, index)
index = index + 1
local value = tbl[index]
if value then
return index, value
end
end
function my_ipairs(tbl)
-- 返回三元组:迭代器函数, 状态, 初始控制变量
return my_ipairs_iterator, tbl, 0
end
-- 使用我们自己的 ipairs
local days = { "Monday", "Tuesday", "Wednesday" }
for index, day in my_ipairs(days) do
print(index, day)
end
-- 输出:
-- 1 Monday
-- 2 Tuesday
-- 3 Wednesday
你看,my_ipairs 完美地遵循了三值协议,for 循环也因此能正确地与它协作。
2. 有状态迭代器(Stateful Iterator)
"有状态"指的是遍历的对象无法作为一个不变的状态 (_state),需要我们添加状态构建一个新的不变的状态 (_state)
lua
-- 这是我们的迭代器函数。
-- 它接收不变的状态表和当前的控制变量。
local function range_iterator(state, current_val)
-- 1. 检查当前值是否已经超出限制
if current_val >= state.limit then
return nil -- 返回 nil 来结束循环
end
-- 2. 计算下一个值
local next_val = current_val + state.step
-- 3. 返回下一个值,它将成为下一次循环的控制变量
return next_val
end
-- 这是我们的迭代器构造函数
function range(start, finish, step)
-- 设置默认值
start = start or 1
finish = finish or 10
step = step or 1
-- 创建包含不变信息的状态表
local state = {
limit = finish,
step = step
}
-- **关键**:返回迭代器三元组
-- 1. 迭代器函数: range_iterator
-- 2. 状态表: state
-- 3. 初始控制变量: 从 start 开始,所以初始值为 start
-- (但为了让第一个返回的值就是 start 本身,
-- 初始控制变量需要是 start - step,这样第一次相加后正好是 start)
return range_iterator, state, start - step
end
print("从 1 到 5,步长为 1:")
for i in range(1, 5) do
print(i)
end
-- 输出:
-- 1
-- 2
-- 3
-- 4
-- 5
在lua中,常用闭包 (Closure)来实现状态的保存,所以有状态迭代器的实现通常使用闭包 实现,闭包实现过程中,没有显式地利用 for 循环传递的 state 和 control_var 参数
lua
function close_iterator(start, finish, step)
start = start - 1
finish = finish or 10
step = step or 1
local current = start -- 保存的状态
return function ()
current = current + step
if current <= finish then
return current
else
return nil
end
end
end
for i in close_iterator(1, 5, 2) do
print(i)
end
结语
点个赞,关注我获取更多实用 Lua 技术干货!如果觉得有用,记得收藏本文!