Lua 基础之迭代器

迭代器与 closure

  • 迭代器是一种可以遍历集合中所有元素的机制,lua 中迭代器使用函数表示,每调用函数一次,返回集合中的下一个元素
  • 迭代器除了定义一个函数之外,还必须知道元素的集合和当前的位置,因此还需要两个外部变量,这点和闭包一样,因此迭代器可以看成是使用闭包实现的
function value(t)
    local i = 0
    return function()
        i = i + 1
        return t[i]
    end
end

t = {1, 2, 3, 4, 5}
-- 创建一个迭代器
iter = value(t)
-- 遍历迭代器
while true do
    local ele = iter()
    if ele == nil then
        break
    end
    print(ele)
end
  • value 是一个工厂,负责生产闭包,这个闭包包括一个匿名函数和外部变量 i 和 t。调用 value 函数之后会生成一个闭包,这个闭包就是我们要的迭代器
  • 使用迭代器的方式也很简单,直接调用迭代器就相当于调用闭包函数,迭代器会返回元素集合的下一个元素,当返回 nil 时就代表元素遍历完了

无状态迭代器

  • 迭代器的使用方法是定义一个工厂,然后利用这个工厂生产闭包,然后调用这个闭包来获取集合中的元素。这种方式的缺点就是每次都得创建一个新的闭包,有一定的开销
  • 泛型 for 循环的设计本身就包含迭代器,而且是一种无状态的迭代器,即不需要创建闭包
  • 泛型 for 循环内部保存了迭代器状态,包括迭代器函数、控制变量和恒定状态
    1. 迭代器函数:迭代器工厂产生的匿名函数,不是闭包
    2. 控制变量:for 循环使用这个变量控制当前遍历的位置及何时结束遍历
    3. 恒定状态:迭代器遍历的目标,一般是指 table

下面我们来解析 ipairs 的实现,看看泛型 for 循环是怎样实现迭代器的

iter = function(t, i)
    i = i + 1
    local v = t[i]
    if v then
        return i, v
    end
end

ipairs = function(t)
    return iter, t, 0
end

for i, v in ipairs(t) do
    print(i, v)
end
  • for 循环执行时会先调用 ipairs(t) ,会返回三个值,这三个值保存在 for 循环内部
    _f, _s, _var = iter, t, 0
  • 然后 for 循环根据控制变量 _var 来调用迭代器函数并保存在变量列表中
    i, v = iter(t, _var)
  • 然后更新控制变量 _var,判断控制变量是否合法,合法则继续调用迭代器函数,不合法则结束
  • for 循环的实现的迭代器跟普通迭代器差不多,不同之处在于控制变量和恒定状态保存是保存 在闭包还是自己保存,普通迭代器需要创建闭包的额外代价

我们可以手动来实现一下泛型 for 循环的实现过程

function for_iterator_print(i, v, t)
    -- 获取迭代函数、恒定状态、控制变量
    local f, s, var = ipairs(t)
    while true do
        -- 调用迭代函数
        i, v = f(s, var)
        -- 更新控制变量
        var = i
        -- 判断控制变量是否合法
        if var == nil then
            break
        end
        print(i, v)
    end
end

使用方式跟 for … in … do 相似

local i, v
for_iterator_print(i, v, t)

pairs 跟 ipairs 差不多,不同的是 pairs 的迭代器函数是 lua 定义的一个 next 函数。
自定义 pairs 的代码如下

local pairs = function(t)
    return next, t, nil
end

local for_iterator_print_pairs
for_iterator_print_pairs = function(k, v, t)
    local f, s, var = pairs(t)
    while true do
        k, v = f(s, var)
        var = k
        if k == nil then
            break
        end
        print(k, v)
    end
end

t = {x = 10, y = 20, z = 30}
local k, v
for_iterator_print_pairs(k, v, t)

总结

  • 泛型 for 实现的迭代器主要保存了 控制变量 恒定状态 迭代器函数 这三个变量,for 循环执行时会先计算 in 后面的表达式,表达式返回的值将赋给那三个变量,接下来的操作就是根据控制变量不断地调用迭代器函数,将得到的结果赋给 in 前面的变量列表
  • 下面的代码是等价的
for k, v in next, t, nil do
    print(k, v)
end

for k, v in pairs(t) do
    print(k, v)
end

多状态迭代器

  • 泛型 for 循环可以实现只有一个恒定状态的迭代器,如果有多个状态需要保存就不行了。如果要保存多个状态,可以有下面两种方式
    1. 使用闭包 colsure
    2. 把多个状态封装在一个 table 里面,然后使用泛型 for 循环
  • 第一种方式会比第二种好点,一方面创建 colsure 的开销比创建 table 的开销要小,另一方面访问非局部变量比访问 table 字段要快
  • 迭代器选择:泛型 for 循环 > colsure > 封装到 table
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值