lua 迭代器和泛型for

2.8k 词
          <h2 id="迭代器和闭包">迭代器和闭包</h2>
  1. 迭代器可以遍历集合的每一个元素,在lua中常常使用函数来描述迭代器,每次调用该函数就返回集合的下一个元素。
  2. 迭代器需要保留上一次成功调用的状态和下一次成功调用的状态,也就是它知道来自于哪里和将要前往哪里。闭包可以实现这个任务。
  3. 闭包是一个内部函数,它可以访问一个或者多个外部函数的局部变量。每次闭包成功调用后这些局部变量都保存他们的值(状态)。
  4. 所以一个典型的闭包结构包含两个函数:一个是闭包自己,另一个是工厂(创建闭包的函数)
--一个简单的迭代器输出table的元素
local function list_iter( t )
    --list_iter是工厂函数
	local i = 0
	local n = table.getn(t)
    --在这里,t、i、n 都属于闭包外部的局部变量
    --返回闭包函数
	return function (  )
		i = i + 1
		if i <= n then
			return t[i]
		end
	end
end

local t = {10,20,30}
local iter = list_iter(t)
while true do
local m = iter()
if m == nil then
break
end
print(m)
end
–输出 10 20 30

泛型for

泛型for在自己内部保存迭代函数,实际上它保存三个值:迭代函数、状态常量、控制变量

for <var-list> in <exp-list> do
    <body>
end
<var-list>是以一个或多个逗号分隔的变量名列表,<exp-list>是以一个或多个逗号分隔的表达式列表,通常情况下exp-list只有一个值:迭代工厂的调用

for k, v in pairs(t) do
print(k, v)
end
kv 为变量列表,pairs(t)为表达式列表

泛型for的执行过程

  • 初始化,计算in后面表达式的值,表达式应该返回泛型for需要的三个值:迭代函数、状态常量、控制变量;如果返回值不够三个用nil补齐,多出则忽略。
  • 将状态变量和控制变量作为参数调用迭代函数(注意:对于for结构来说,状态常量没有用处,仅仅在初始化的时候获取值并传递给迭代函数)。
  • 将迭代函数返回的值赋给变量列表。
  • 如果返回的第一个值为nil,循环结束,否则执行循环体。
  • 回到第二步再次调用迭代函数。
for var1, ..., varn in exp-list do
    block
end
等价于
do
    local _f, _s, _var = exp-list
    --_f 是迭代函数,_s 是状态常量,_var 控制变量
    while true do
        local var1, ..., varn = _f(_s, _var)
        _var = var1
        if _var == nil then
            break
        end
        block
    end
end

–简单例子
local function square( state, control )
if state <= control then
return
end
control = control + 1
return control, control*control
end
– 一开始 state = 9, control = 0,
– state不变,而控制变量 control 等于square的第一个返回值,不断累加 1
for k,v in square,9,0 do
print(k,v)
if k == nil then
break
end
end

pairs

function pairs(t)
    return function ( tbl, k )
    	k, v = next(tbl, k)
    	print("iter ", k,v)
    	if v then
    		return k, v
    	end
    end, t, nil
end

local tt = {}
tt[1] = 1
tt[2] = 2
tt[3] = nil
tt[4] = 4

–for k,v in pairs(tt) do
– print(k,v)
–end

do
local _f, _s, _var = pairs(tt) – _f 是迭代函数,_s 是状态变量,要迭代的对象,即传入的tt,_var 是控制变量
while true do
local k, v = _f(_s, _var) –执行迭代函数,返回 k,v,迭代结束时两个都是nil
print(“call iter :”,k, v)
_var = k –刷新控制变量
if _var == nil then
break
end
print(“call block :”, k, v)
end
end

–输出
–[[
iterator : 1 1
call iter : 1 1
call block : 1 1
iterator : 2 2
call iter : 2 2
call block : 2 2
iterator : 4 4
call iter : 4 4
call block : 4 4
iterator : nil nil
call iter : nil nil
]]

ipairs

local tt = {}
tt[1] = 1
tt[2] = 2
tt[3] = nil
tt[4] = 4

function ipairs(t)
return function(a, i)
i = i + 1
if a[i] then
–从这里可以看出,tt[4]不会被输出了,某个下标的元素不存在就会中断
print("iterator : ", i, a[i])
return i, a[i]
end
end, t, 0
end

– for i,v in ipairs(tt) do
– print(i,v)
– end

do
local _f, _s, _var = ipairs(tt) –_f 是迭代函数,_s 是状态变量,要迭代的对象,即传入的tt,_var 是控制变量
while true do
local i, v = _f(_s, _var)
print("call iter : ", i, v)
_var = i
if _var == nil then
break
end
print("call block : ",i, v)
end
end

–输出
–[[
iterator : 1 1
call iter : 1 1
call block : 1 1
iterator : 2 2
call iter : 2 2
call block : 2 2
call iter : nil nil
]]

next

next 函数原型是 next( table [ , index] )

table 是要遍历的表

index :next返回的值即是index索引的下一个值,当index为nil时,将返回第一个索引的值,当索引号为最后一个索引或者表为空时返回nil

常用 next(table) 是否返回nil来判断表是否是空表

local a = {a = "a", b = "b", c = "c", d = "d"}
local value
while next(a, value) do
    print(next(a, value))
    value = next(a, value)
end

–输出
–[[
a a
d d
c c
b b
]]