Lua迭代器

迭代器(iterator)是一种可以遍历一个集合中所有元素的代码结构。需要在连续的调用之间保存一些状态,这样才能知道当前迭代器所处的位置及如何从当前位置步进到下一个位置。

迭代器这个名字多少有点误导性,因为迭代器并没有进行实际的迭代:真正的迭代是循环体完成的,迭代器只不过为每次的迭代提供连续的值。叫“生成器”(generator)可能更好。

lua使用闭包来表示迭代器。闭包是一个可以访问其自身环境中一个或多个局部变量的函数。这些变量将连续调用过程中的值保存起来,使得闭包能够记住迭代所处的位置。

闭包通常涉及两个函数:闭包本身和一个用于创建该闭包及其upvalue的工厂(factory)。

function factory(t)  --用于创建该闭包及其upvalue的工厂
  local i = 0   -- upvalue
  return function()  -- 闭包
    i = i + 1
    return i
  end
end

pairs

对一个table执行pairs函数调用,会有3个返回值:迭代函数,当前table和初始key。泛型for循环内部正是用这3个值来实现对当前table的迭代。

local t1 = {1,8,h1=nil,h2="earth",5}
print("[t1] => "..tostring(t1))  -- [t1] => table: 000002C4F4410470

local a1,a2,a3 = pairs(t1)
print("[a1] => "..tostring(a1)) -- [a1] => function: 00007FF9A60391B0  即迭代函数   对应名为int luaB_next (lua_State *L)的c函数
print("[a2] => "..tostring(a2)) -- [a2] => table: 000002C4F4410470
print("[a3] => "..tostring(a3)) -- [a3] => nil  即初始key

特点:

① 可以遍历table中的所有值不为nil的元素

② 不保序:遍历的顺序是随机的

local t1 = {1,8,h1=nil,h2="earth",5}
local t2 = {k1="go",k2=100}
local t3 = {10,20,nil,30}
local t4 = {g1="sun",25,30,nil,35}

print("table t1's length:"..#t1)
for i,v in pairs(t1) do   -- 对应泛型for,返回的第一个变量i为nil时,循环终止
    print("["..i.."] => "..tostring(v))
end
print("-----------")

print("table t2's length:"..#t2)
for i,v in pairs(t2) do
    print("["..i.."] => "..tostring(v))
end
print("-----------")

print("table t3's length:"..#t3)    -- 用while循环来模拟泛型for
local iter,b,c = pairs(t3) -- iter对应名为int luaB_next (lua_State *L)的c函数  b为t3表  c为nil
while true do
    local i,v = iter(b,c)
    if (i==nil) then
        break 
    end
    print("["..i.."] => "..tostring(v))

    c = i
end
print("-----------")

print("table t4's length:"..#t4)  -- 用while循环来模拟泛型for
local m, n
local iterr = pairs(t4)
while true do
    m, n = iterr(t4,m)
    if (m==nil) then
        break 
    end
    print("["..m.."] => "..tostring(n))
end
print("-----------")

输出结果如下:

table t1's length:3
[1] => 1
[2] => 8
[3] => 5
[h2] => earth
-----------
table t2's length:0
[k2] => 100
[k1] => go
-----------
table t3's length:4
[1] => 10
[2] => 20
[4] => 30
-----------
table t4's length:4
[1] => 25
[2] => 30
[4] => 35
[g1] => sun
-----------

实现自己的pairs闭包

local t1 = {1,8,h1=nil,h2="earth",5}
local t2 = {k1="go",k2=100}
local t3 = {10,20,nil,30}
local t4 = {g1="sun",25,30,nil,35}

function mypairs(t)
    return next, t, nil
end

print("table t1's length:"..#t1)
for i,v in next, t1, nil do  -- 对应泛型for,返回的第一个变量i为nil时,循环终止
    print("["..i.."] => "..tostring(v))
end
print("-----------")

print("table t2's length:"..#t2)
for i,v in mypairs(t2) do
  print("["..i.."] => "..tostring(v))
end
print("-----------")

print("table t3's length:"..#t3)  -- 用while循环来模拟泛型for
local myiter,b,c = mypairs(t3)
while true do
    local i,v = myiter(b,c)
    if (i==nil) then
        break 
    end
    print("["..i.."] => "..tostring(v))

    c = i
end
print("-----------")

print("table t4's length:"..#t4)  -- 用while循环来模拟泛型for
local m, n
while true do
    m, n = next(t4,m)
    if (m==nil) then
        break 
    end
    print("["..m.."] => "..tostring(n))
end
print("-----------")

ipairs

对一个table执行ipairs函数调用,会有3个返回值:迭代函数,当前table和初始索引。泛型for循环内部正是用这3个值来实现对当前table的迭代。

local t1 = {1,8,h1=nil,h2="earth",5}
print("[t1] => "..tostring(t1))  -- [t1] => table: 0000019EC28B8020

local a1,a2,a3 = ipairs(t1)
print("[a1] => "..tostring(a1)) -- [a1] => function: 00007FF9A0559410  即迭代函数   对应名为static int ipairsaux (lua_State *L)的c函数
print("[a2] => "..tostring(a2)) -- [a2] => table: 0000019EC28B8020
print("[a3] => "..tostring(a3)) -- [a3] => 0  即初始索引

特点:

① 只能遍历table中的数组元素

② table中某个数组元素为nil,则table的遍历会终止

③ 保序:遍历的顺序与存放的顺序一致

local t1 = {1,8,h1=nil,h2="earth",5}
local t2 = {k1="go",k2=100}
local t3 = {10,20,nil,30}
local t4 = {g1="sun",25,30,nil,35}

print("table t1's length:"..#t1)
for i,v in ipairs(t1) do   -- 对应泛型for,返回的第一个变量i为nil时,循环终止
  print("["..i.."] => "..tostring(v))
end
print("-----------")

print("table t2's length:"..#t2)
for i,v in ipairs(t2) do
    print("["..i.."] => "..tostring(v))
end
print("-----------")

print("table t3's length:"..#t3)    -- 用while循环来模拟泛型for
local iter,b,c = ipairs(t3) -- iter对应名为static int ipairsaux (lua_State *L)的c函数  b为t3表  c为0
while true do
    local i,v = iter(b,c)
    if (i==nil) then
        break 
    end
    print("["..i.."] => "..tostring(v))

    c = c + 1
end
print("-----------")

print("table t4's length:"..#t4)  -- 用while循环来模拟泛型for
local m = 0
local n = nil
local iterr = ipairs(t4) -- iter对应名为static int ipairsaux (lua_State *L)的c函数
while true do
    m, n = iterr(t4,m)
    if (m==nil) then
        break 
    end
    print("["..m.."] => "..tostring(n))
end
print("-----------")

输出结果如下:

table t1's length:3
[1] => 1
[2] => 8
[3] => 5
-----------
table t2's length:0
-----------
table t3's length:4
[1] => 10
[2] => 20
-----------
table t4's length:4
[1] => 25
[2] => 30
-----------

实现自己的ipairs闭包(方式①)

local t1 = {1,8,h1=nil,h2="earth",5}
local t2 = {k1="go",k2=100}
local t3 = {10,20,nil,30}
local t4 = {g1="sun",25,30,nil,35}

function myipairs(t)
    local i = 0
    return function()
        i = i + 1
        if (t[i]~=nil) then
            return i, t[i]
        end
    end
end

print("table t1's length:"..#t1)
for i,v in myipairs(t1) do  -- 对应泛型for,返回的第一个变量i为nil时,循环终止
    print("["..i.."] => "..tostring(v))
end
print("-----------")

print("table t2's length:"..#t2)
for i,v in myipairs(t2) do
    print("["..i.."] => "..tostring(v))
end
print("-----------")

print("table t3's length:"..#t3)
for i,v in myipairs(t3) do
    print("["..i.."] => "..tostring(v))
end
print("-----------")

print("table t4's length:"..#t4)  -- 用while循环来模拟泛型for
local myiter = myipairs(t4)
while true do
    local i,v = myiter()
    if (i==nil) then
        break 
    end
    print("["..i.."] => "..tostring(v))
end
print("-----------")

实现自己的ipairs闭包(方式②)

local t1 = {1,8,h1=nil,h2="earth",5}
local t2 = {k1="go",k2=100}
local t3 = {10,20,nil,30}
local t4 = {g1="sun",25,30,nil,35}

function myiter(t, i)
    i = i + 1
    if (t[i]~=nil) then
        return i, t[i]
    end
end

function myipairs2(t)
    return myiter, t, 0
end

print("table t1's length:"..#t1)
for i,v in myiter, t1, 0 do  -- 对应泛型for,返回的第一个变量i为nil时,循环终止
    print("["..i.."] => "..tostring(v))
end
print("-----------")

print("table t2's length:"..#t2)
for i,v in myipairs2(t2) do
  print("["..i.."] => "..tostring(v))
end
print("-----------")

print("table t3's length:"..#t3)  -- 用while循环来模拟泛型for
local myiter,b,c = myipairs2(t3)
while true do
    local i,v = myiter(b,c)
    if (i==nil) then
        break 
    end
    print("["..i.."] => "..tostring(v))

    c = c + 1
end
print("-----------")

print("table t4's length:"..#t4)  -- 用while循环来模拟泛型for
local m = 0
local n = nil
while true do
    m, n = myiter(t4, m)
    if (m==nil) then
        break
    end
    print("["..m.."] => "..tostring(n))
end
print("-----------")