A function in Lua is a first-order type value (first-class value), and defining a function is like creating a normal type value (except that the data for a function type value is primarily an instruction), so the function can still be defined in the body of the function. Assuming that the function F2 is defined in the function F1, then it is called F2 as F1 embedded (inner) function, F1 as F2 of the outsourcing (enclosing) function, outsourcing and embedding are transitive, that is F2 embedded must be F1 embedded, and F1 outsourcing must be F2 outsourcing. Inline functions can access all the local variables that the outsourced function has created, the so-called word legal bounds (lexical scoping), which are called external local variables (external local variable) or upvalue of the inline function. Look at the following code:
function F1 (n)
--function parameters are also local variables
Local function F2 ()
Print (n)--a local variable referencing an outsourced function
End
return F2
End
G1 = F1 (1979)
G1 ()--Print out 1979
G2 = F1 (500)
G2 ()--Print out 500
When G1 = F1 (1979) is executed, the life of the local variable n is supposed to end, but since he has become the upvalue of the inline function F2 (which he is assigned to the variable G1), he can still continue to "survive" in some form, thus allowing G1 () to print the correct value.
Why G2 and G1 function bodies are the same (all functions that are F1 inline functions F2), but the print values are different. This involves a fairly important concept-closure (closure). In fact, when Lua compiles a function, it generates a prototype for him (prototype), which contains the virtual machine instruction for the function body, the constant value (number, text string, and so on) used by the function, and some debugging information. At run time, whenever Lua executes an expression like function...end, he creates a new data object that contains a reference to the corresponding function prototype, an environment (environment, a table for finding global variables) Reference and an array of all upvalue references, and this data object is called a closure. Thus, the function is a compile-time concept, is static, and the closure is the concept of runtime, is dynamic. The value of G1 and G2 is strictly not a function but a closure, and two different closures, and each closure can retain its own upvalue value, so G1 and G2 will certainly not print the same result.
The use of Upvalue is very convenient, but their semantics are also very subtle and need to be noticed. For example, change the F1 function to:
function F1 (n)
Local function F2 ()
Print (n)
End
n = n + 10
return F2
End
G1 = F1 (1979)
G1 ()--Print out 1989
The inline function is defined before the N = n + 10 statement, but why G1 () prints 1989. Upvalue is actually a local variable, and the local variable is stored on the function stack frame (stack frame), so as long as Upvalue has not left its scope, he has been living on the function stack. In this case, the closure will access them through a reference to the Upvalue on the stack, and once upvalue is about to leave its scope (which also means that he is going to disappear from the stack immediately), the closure allocates space for him and saves the current value. The upvalue can then be accessed by referencing a reference to the newly allocated space. When execution to F1 (1979) n = n + 10 o'clock, the closure has been created, but N does not leave the scope, so the closure still refers to N on the stack, when the return F2 completes, n is about to end the life, when the closure will be N (already 1989) copied to the space in their own management for future access. Once the internal secrets have been clarified, the results are not difficult to explain.
Upvalue can also provide a mechanism for data sharing between closures. The following 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 ()--Print 1979
F2 ()
F1 ()--Print 1989
F2 ()
F1 ()--Print 1999
The f1,f2 of the two closures are the embedded functions foo1 and Foo2 in Create, while the Foo2 referenced by Foo1 and Upvalue are the same, that is, the local variable n of Create. As I said earlier, after executing the Create call, the closure will copy the value of n on the stack, then whether F1 and F2 have a copy of n respectively. In fact, when Lua discovers that the upvalue of two closures point to the same variable on the current stack, it intelligently generates only one copy, and then lets the two closures share the copy, so that any closure changes to the upvalue will be detected by another. The example above illustrates this very clearly: 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, allowing the closure to communicate without relying on global variables, thus greatly improving the reliability of the code.
It is also possible for a closure to be upvalue on the stack at the time it was created because the inline function can refer to the local variables of the outer outsourced 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 (1979)
F1,F2 = t ()
F1 ()--Print 1979
F2 ()
F1 ()--Print 1989
G1,G2 = t ()
G1 ()--Print 1989
G2 ()
G1 ()--Print 1999
F1 ()--Print 1999
After executing t = Test (1979), the local variable N of test is "dead", so when the F1,F2 two closures are created, no traces of n are found on the stack, which is how they get the value of N. Oh, do not forget the test function of n is not only Inner1 and Inner2 Upvalue, and he is also Foo's upvalue. t = Test (1979), t this closure must have been properly preserved n, then F1, F2 if not found on the current stack n will automatically go to their outsourced closure (so called) Upvalue reference array to find, and copy the found reference value into its own Upvalue reference array. A closer look at the above code will determine that G1 and G2 and F1 and F2 share the same upvalue. What is this for? In fact, G1 and G2 and F1 and F2 are all created with the same closure (t), so the Upvalue (n) They refer to is actually the same variable, and the search mechanism just described ensures that their upvalue references will point to the same place at the end.
LUA functions as a basic type value and supports the nature of the word's legal boundaries, which makes the language powerful abstract ability. And a thorough understanding of functions, closures, and Upvalue will help programmers use this ability.