阅读 110

lua闭包函数与泛型for

闭包函数

lua闭包简单的说就是一个函数内定义的新函数可以访问该函数内部的任意变量。 例如:

function add()
    local count = 1
    return function() ---假设这个匿名函数为a
        count = count + 1
        return count
    end
end
复制代码

当我们调用add函数时,会返回匿名函数a的地址,如下:

print(add())
-- 输出结果:function: 00000253D917C2C0
复制代码

若将匿名函数复制给一个局部变量,然可以通过该局部变量调用该函数,当然也可以直接调用闭包中的匿名函数:

print(add()())
-- 等价于
local a = add()
print(a())
复制代码

闭包函数有点像函数调用,

function add()
    local count = 1
    return function()
        count = count + 1
        return count
    end
end

function a()
    return 233
end

function b()
    return a()
end

print(b())
print(add())
复制代码

不同点是add函数返回的是函数地址,而b函数返回的是函数执行完后的结果(尾调用:函数的返回值,除了调用应一个函数外,不做其他的任何操作)。闭包函数类似于下面这种方式(不等于,因为a中不能访问b中的变量)


function a()
    return 233
end

function b()
    return a
end
复制代码

泛型for

泛型for由三部分组成:迭代函数,不可变量,控制变量。其大致语法如下:

for <var-list> in <exp-list> do
     <body>
end
复制代码
  • 其中 为变量列表,把变量列表的第一个变量称之为控制变量,当控制变量为nil的时候,循环终止。
  • 为表达式列表,可以用一个闭包函数代替(例如ipairs函数),-因为运行开始时for首先会对in后面的表达式求值,所以此时闭包函数只需要能够返回三个值:迭代函数,不可变量,和控制变量的初始值即可。每次循环,就相当于把不可变状态和上一次从的控制变量的值作为值,传递给迭代函数,并运行一次迭代函数。

ipairs是我们最常用的遍历数组的方式,例如:

local tab = 
{
    [1] = 1,
    [2] = 2,
    [3] = 3,
    [5] = 5,--中间空出一个si
}
for k,v in ipairs(tab) do
    print(k,v)
end
-- print:
-- 1	1
-- 2	2
-- 3	3
复制代码

我们用如下方式也可以实现类似的功能:

local tab = 
{
    [1] = 1,
    [2] = 2,
    [3] = 3,
    [5] = 5,--中间空出一个si
}

function temp(tab,index)
    index = index + 1
    if tab[index] then
        return index,tab[index]
    end
end

function iter(tab)
    return temp,tab,0
end

for k,v in iter(tab) do
    print(k,v)
end
复制代码
  • iter和ipairs一样是个闭包函数,for运行开始时,首先会运行iter函数(也就是初始化in后面的表达式),返回一个闭包(temp),一个不可变量(tab),一个控制变量初始值(0)
  • 由迭代函数temp可以看出,当tab表中不具有某个key时,实际上返回的是nil,nil,只是后会终止程序。所以当运行到index = 4是,tab[4] 为nil所以会终止程序。
  • 这也是为什么ipairs只可以遍历连续数组的原因。

与ipairs对应的是pairs,其使用的迭代函数为lua提供的next函数。next函数接收一个table,返回一个key和value,与ipairs一样,当key为nil的时候,程序终止。但是pairs遍历表并没有按照顺序遍历,所以有时候需要我们转换一下思考方式例如:

local tab = 
{
    [2] = 'g',
    [4] = 'j',
    [6] = 'a',
    [9] = 'b'
}
--要求按value的ASCII码大小输出

local temp = {}
for k,v in pairs(tab) do
    temp[#temp + 1] = v
end

table.sort(temp,function(a,b)
   return a < b 
end)

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

-- 1	a
-- 2	b
-- 3	g
-- 4	j
复制代码

另外我们可以用next函数判断某个表是否为空表

local tab = 
{
    [2] = 'g',
    [4] = 'j',
    [6] = 'a',
    [9] = 'b'
}

local t = {}

print(next(t) == nil) -- true
print(next(tab) == nil) --false
复制代码

千万不要用

local t = {}
if t == {} then
    print("t为空")
end
复制代码

这种方式判断某个表为空,因为毫无意义