lua中的闭包(closure或者匿名函数)的作用

闭包和upvalue

lua构建一个新的函数时, 会创建一个函数对象, 其中包含了函数本身的原型还有函数使用到的外部变量或者常量. 当这个新的函数处于某个函数内部, 我们将这个对象称之为闭包, 它所引用的外部函数的局部变量叫做upvalue.

下面是一个例子:

function f1(n)
    local upvalue = "hello"
    local f2 = function()
        print(upvalue .. " " .. n)
    end

    return f2
end

g1 = f1(500)
g1()
print(g1)

g2 = f1(1500)
g2()
print(g2)


$ luajit copy.lua 
$ hello 500
$ function: 0x00921ae0
$ hello 1500
$ function: 0x00926f28

每次调用f1将生成一个以f2为函数原型, 加上两个upvalue值(n, upvalue)的新函数(闭包). 每个新的闭包保持同一函数原型, 分别保存不同的upvalue. 严格来说, 闭包属于动态的概念, 是在程序运行阶段确定的.

upvalue值的作用域

upvalue实际上是闭包的外部函数的局部变量, 存在于外部函数的堆栈中, 在外部函数的执行过程中, 闭包通过访问外部函数堆栈的upvalue引用来访问upvalue, 一旦外部函数的调用过程完毕, 函数的堆栈被清空, upvalue也会被清除, 此时闭包会将upvalue复制到自己的函数堆栈中以便使用. 外部函数(也可以是闭包)执行完毕之前, 内部的闭包和其子闭包使用的都是外部函数堆栈中的那一份, 任何修改都会影响所有的函数(外部函数, 闭包和其子闭包), 当外部函数执行完毕后, 内部的闭包和其子闭包使用的是内部闭包从外部函数复制的那一份, 可以以此类推到最里层的闭包. 下面举例说明.

function f1(n)
    local upvalue = "hello"

    local f0 = function()
        local f2 = function()
            n = n * 2
            print(upvalue .. " f2 " .. n)
        end
    
        local f3 = function()
            print(upvalue .. " f3 " .. n)
        end

        print(upvalue .. " f0 " .. n)
    
        return f2, f3
    end

    g1, g2 = f0()
    g1()
    g2()

    print(upvalue .. " f1 " .. n)

    return f0
end

g0 =  f1(500)

$ luajit copy.lua 
$ hello f0 500
$ hello f2 1000
$ hello f3 1000
$ hello f1 1000

f1是外部函数, n是其内部变量, 也是内部的f2, f3形成的闭包的upvalue. 要注意的是, 每次进入f1时, 变量n都是不同的, 生成的闭包共享和保存的是当次进入外部函数时的局部变量.

闭包的作用

闭包的主要作用有两个, 一是简洁, 不需要在不使用时生成对象, 也不需要函数名, 二是可以捕获外部变量形成不同的调用环境. 从其名称可以看出来, 闭包的最主要作用是后者, 即可以捕获外部变量.

下面举例说明:

function f1()
    local i = 0

    return function()
        i = i + 1
        return i
    end
end

g1 = f1()
g2 = f1()

print("g1 " .. g1())
print("g1 " .. g1())

print("------------------------")

print("g2 " .. g2())
print("g2 " .. g2())

$ luajit test.lua 
> g1 1
> g1 2
> ------------------------
> g2 1
> g2 2

每次调用f1时会生成不同的局部变量i(值为函数定义时的初始值), 此时闭包将i保存和使用, 与另外的闭包独立开来. g1和g2共享一份函数定义和upvalue初始值, 但是调用环境独立.

这种特性最典型的使用场景是迭代器:

function f1(t)
    local i = 0
    return function()
        i = i + 1
        return t[i]
    end
end

t = {"111", "heell", 5, 6, "3333"}
for e in f1(t) do
    print(e)
end

$ luajit test.lua 
> 111
> heell
> 5
> 6
> 3333

猜你喜欢

转载自blog.csdn.net/woodengm/article/details/109764026