Lua 基础之迭代器

来源:互联网 发布:xmind mac 序列号 编辑:程序博客网 时间:2024/06/01 12:08

迭代器与 closure

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

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

local i, vfor_iterator_print(i, v, t)

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

local pairs = function(t)    return next, t, nilendlocal for_iterator_print_pairsfor_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)    endendt = {x = 10, y = 20, z = 30}local k, vfor_iterator_print_pairs(k, v, t)

总结

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

多状态迭代器

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