Learning the closure in Lua

Source: Internet
Author: User

In Lua, a function is a first-class value. Defining a function is like creating a common type value (except that the data of a function type value is mainly a command ), therefore, functions can still be defined in the function body. Assume that the function F2 is defined in the function F1, then it is called F2 as an inner function of F1, and F1 is an outsourcing function of F2. Both outsourcing and embedding are passed, that is, F2 embedding must be F1 embedding, while F1 outsourcing must be F2 outsourcing. Embedded functions can access all local variables created by external functions. This feature is called lexical scoping ), these local variables are called the external local variables or upvalue of the embedded function (the word number may be misleading, because upvalue actually refers to the variable rather than the value ). Compile the following code:

Function F1 (N)
-- The function parameter is also a local variable.

Local function F2 ()
Print (n) -- reference local variables of outsourcing functions
End
Return F2
End

G1 = F1 (0, 1979)
G1 () -- print 1979
G2 = F1 (500)
G2 () -- print 500

After G1 = F1 (1979) is executed, the life of the local variable N should have ended, but it has become the upvalue of the embedded function F2 (which is assigned to the variable G1, so it can still "survive" in some form, so that G1 () can print the correct value.


But why does G2 have the same function body as G1 (both are the built-in function bodies of F1 F2), but the print value is different? This involves a very important concept-closure ). Fact
When Lua compiles a function, it generates a prototype for it, which contains the VM commands corresponding to the function body and the constant values (numbers, text strings, etc.) used by the function) and
Debugging information. At runtime, Every time Lua executes a function, such as function... end
When such an expression is used, it creates a new data object, which contains the reference and environment of the corresponding function prototype (environment, used to find the global variable table) and
An array composed of upvalue references. This data object is called a closure. It can be seen that the function is the concept of compile-time, static, and closure is the concept of runtime and dynamic. The G1 and G2 values are strict.
It is not a function but a closure, and it is two different closures. Each closure can have its own upvalue value, so the output of G1 and G2 is certainly different. Although closures and functions
They are essentially different concepts, but we do not differentiate them for convenience and without confusion.

Upvalue is very convenient to use, but their semantics is also very subtle and should be noted. For example, change the F1 function:

Function F1 (N)
Local function F2 ()
Print (N)
End
N = N + 10
Return F2
End

G1 = F1 (0, 1979)
G1 () -- print 1989

Nested functions are defined in n = N +
10 before this statement, why does G1 () print 1989? Upvalue is actually a local variable, while a local variable is saved on the function stack framework (Stack
Frame), so as long as the upvalue has not left its own scope, it will survive on the function stack. In this case, the closure will be referenced by the upvalue pointing to the stack.
Access them. Once upvalue is about to exit its own scope (which means it will disappear from the stack immediately), the closure will allocate space for it and save the current value, you can then point to the new blank
To access the upvalue. When the execution to F1 (1979) n = N +
10, the closure has been created, but n does not leave the scope, so the closure still references N on the stack, when return
When F2 is complete, n is about to end. At this time, the closure copies N (which is already 1989) to the self-managed space for future access. After figuring out the internal secrets, it is not difficult to explain the running results.

Upvalue can also provide a data sharing mechanism between closures. Example:

Function create (N)
Local function foo1 ()
Print (N)
End

Local function foo2 ()
N = N + 10
End

Return foo1, foo2
End

F1, F2 = create (1979)
F1 () -- prints 1979
F2 ()
F1 () -- prints 1989
F2 ()
F1 () -- prints 1999

The prototypes of the closures F1 and F2 are the built-in functions foo1 and foo2 in create, and the upvalue referenced by foo1 and foo2 are the same, that is
Create local variable n. As mentioned above, after executing the create call, the closure will copy the N value on the stack. Does F1 and F2 have an n Copy respectively? Actually not
However, when Lua finds that the upvalue of the two closures points to the same variables on the current stack, it will intelligently generate only one copy, and then let the two closures share the copy, in this way, any closure
The modification of upvalue will be further explored. The above example clearly demonstrates this: each call to F2 increases the value of upvalue by 10, and then F1 prints the updated value.
This Semantics of upvalue is very valuable. It enables the closure to communicate with each other without relying on global variables, thus greatly improving the code reliability.

When a closure is created, its upvalue is no longer on the stack. This is because the nested function can reference the local variables of the outer outsourcing function:

Function Test (N)
Local function Foo ()
Local function inner1 ()
Print (N)
End
Local function inner2 ()
N = N + 10
End
Return inner1, inner2
End
Return foo
End

T = test (1, 1979)
F1, F2 = T ()
F1 () -- prints 1979
F2 ()
F1 () -- prints 1989
G1, G2 = T ()
G1 () -- prints 1989
G2 ()
G1 () -- prints 1999
F1 () -- prints 1999

Executed T =
After test (1979), the local variable n of test is "dead", so when the F1 and F2 closures are created, N cannot be found on the stack, how do they obtain N values? Ah
Well, don't forget that the n of the test function is not only the upvalue of inner1 and inner2, but also the upvalue of foo. T =
After test (1979), the T closure must have saved n properly, if n cannot be found on the current stack, F1 and F2 will automatically go to their outsourcing closure (so called ).
Upvalue references the array and copies the referenced value to its upvalue reference array. By carefully observing the above code, we can determine that G1 and G2 share the same one with F1 and F2.
Upvalue. Why? Actually, G1 and G2 are created with the same closure (T) as F1 and F2, so the upvalue (n) They reference is actually the same variable.
The search mechanism described above ensures that all upvalue references in the end point to the same place.

Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.