循环背后的魔法:Lua 迭代器深度解析

一、泛型 for 循环的解构

要理解迭代器,我们首先必须拆解泛型 for 循环的语法: for var_1, var_2, ..., var_n in <表达式列表> do ... end

这里的关键是 <表达式列表>。当循环开始时,Lua 会对这个列表求值一次,并期望它返回三个值

  1. 迭代器函数(Iterator Function) :一个函数,for 循环在每次迭代时都会调用它来获取下一个(或下一组)值。
  2. 不变的状态(Invariant State):一个值(通常是表),它会在整个循环过程中被传入迭代器函数,用于维持状态。它本身通常不改变。
  3. 初始控制变量(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 内置的 ipairspairs 就是典型的无状态迭代器。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 循环传递的 statecontrol_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 技术干货!如果觉得有用,记得收藏本文!

相关推荐
天才奇男子5 分钟前
从零开始搭建Linux Web服务器
linux·服务器·前端
长空任鸟飞_阿康22 分钟前
AI 多模态全栈应用项目描述
前端·vue.js·人工智能·node.js·语音识别
Mintopia24 分钟前
🌐 实时协同 AIGC:多人在线 Web 创作的技术架构设计
前端·人工智能·trae
Mintopia27 分钟前
🔥 “Solo Coding”的近期热度解析(截至 2025 年末)
前端·人工智能·trae
顾安r1 小时前
11.14 脚本网页游戏 猜黑红
前端·javascript·游戏·flask·html
码码哈哈0.01 小时前
Vue 3 + Vite 集成 Spring Boot 完整部署指南 - 前后端一体化打包方案
前端·vue.js·spring boot
@菜菜_达2 小时前
interact.js 前端拖拽插件
开发语言·前端·javascript
Baklib梅梅2 小时前
故事叙述的力量:用Baklib创作让内容更具温度与共鸣
前端·ruby on rails·前端框架·ruby
一个假的前端男2 小时前
uniapp 3端轮播
前端·javascript·uni-app
Fantasydg4 小时前
Request Response对象
前端