The role of closures (closure or anonymous functions) in Lua

Closure and upvalue

When Lua builds a new function, it creates a function object, which contains the prototype of the function itself and the external variables or constants used by the function. When the new function is inside a function, we call this object Closure, the local variable of the external function it refers to is called upvalue.

Below is an example:

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

Each call to f1 will generate a new function (closure) with f2 as the function prototype, plus two upvalues ​​(n, upvalue). Each new closure keeps the same function prototype and stores different upvalues. Strictly In other words, closure is a dynamic concept, which is determined during the running phase of the program.

Scope of upvalue value

Upvalue is actually a local variable of the external function of the closure, which exists in the stack of the external function. During the execution of the external function, the closure accesses the upvalue by accessing the upvalue reference of the external function stack. Once the external function's calling process is completed , The stack of the function is cleared, and the upvalue is also cleared. At this time, the closure will copy the upvalue to its own function stack for use. Before the external function (or closure) is executed, the internal closure and its children The closure uses the part of the external function stack. Any modification will affect all functions (external function, closure and its sub-closures). When the external function is executed, the internal closure and its sub-closures The package uses the copy of the inner closure from the outer function, which can be deduced by analogy to the innermost closure. The following is an example.

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 is an external function, n is its internal variable, and it is also the upvalue of the closure formed by the internal f2 and f3. It should be noted that each time you enter f1, the variable n is different, and the generated closure is shared and saved It is the local variable when entering the external function .

The role of closures

There are two main functions of closures. One is conciseness, no need to generate objects when not in use, and no need for function names, and the other is to capture external variables to form different calling environments. As can be seen from its name, the closure is The main function is the latter, that is, it can capture external variables.

The following example illustrates:

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

Each time f1 is called, a different local variable i is generated (the value is the initial value when the function is defined). At this time, the closure saves and uses i, independent of other closures. g1 and g2 share a function definition It is the initial value of upvalue, but the calling environment is independent.

The most typical use case for this feature is iterators:

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

 

Guess you like

Origin blog.csdn.net/woodengm/article/details/109764026