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("-----------")