The function in Lua is a first-class value. The definition function is like creating a normal type value (only the data of the function type value is mainly a provision of instructions), so the function can still be defined in the function body. . Suppose the function F2 defines in the function F1, then the inner function of F2 is F1, the ouclosing function of F1 is F2, the outer package, and the embedding have passed, that is, F2 is inevitable. The embedded, and the outer extension of F1 is also an outsourcing of F2. The embedded function can access all local variables that the outer bag function has created. This feature is the so-called lexical scoping, which is called the external partial variable of the inner function (External Local Variable) or UpValue (this word will make people misunderstand because UpValue actually refers to variables rather than the value). Try to see the following code:
Function F1 (N) - Function parameters are also partial variables
Local Function F2 () Print (n) - Local variables of the outsourcing function END RETURN F2END
G1 = F1 (1979) G1 () - Print 1979G2 = F1 (500) G2 () - Print 500
When the G1 = F1 (1979) is performed, the life of the local variable N is ended, but because it has become UpValue, which has become an inline function F2 (it is also assigned to the variable G1), it can still be in some kind The form continues to "survive" down, so that G1 () prints the correct value.
Can you be the same as the G1 function body (all the function of F1's inline function F2), but the printed value is different? This involves a fairly important concept - Closure (Closure). In fact, when Lua recompores a function, generates a prototype, which contains virtual machine instructions corresponding to the function body, a constant value (number, text string, etc.) and some debugging information for functions. At runtime, whenever LUA performs an expression such as function ... END, it creates a new data object, which contains references to the corresponding function prototype, environment (Environment, used to find global variables) Table) The reference and an array consisting of all UpValue references, and this data object is called a closure. It can be seen that the function is a compile period concept, and it is static, and the closure is the concept of running period. It is dynamic. The values of G1 and G2 are strictly not a function but closer, and two different closures, and each closure can hold their own UpValue value, so the results printed by G1 and G2 are of course not the same. . Although closures and functions are different concepts, for convenience, we don't distinguish them without confusion.
It is very convenient to use UpValue, but their semantics are also very subtle, need to be noticed. For example, change the F1 function:
FUNCTION F1 (N) Local Function F2 () Print (n) end n = n 10 Return F2end
G1 = f1 (1979) G1 () - Print 1989
The embedded function is defined before n = n 10 statement, can you print it? UpValue is actually local variables, and local variables are saved on the Function Stack Frame, so as long as UpValue has not left its own scope, it has been survive on the function stack. In this case, the closure will access them by pointing to UpValue on the stack, once UpValue will leave your own scope (this also means that it will disappear from the stack), the closure will allocate space for it. And save the current value, you can access the UpValue by pointing to a reference to the new allocation space. When the N = N 10 of F1 (1979) is executed, the closure has been created, but N does not leave the scope, so the closure still references N on the stack. When returnif is completed, n is about to end life. At this time, the closure will copy n (already 1989) to the space you manage to access. After you understand the inside secret, it is not difficult to explain. UpValue can also provide a mechanism for data sharing between the closure. Try this example:
Function Create (N) Local Function Foo1 () Print (n) End
Local function foo2 () n = n 10 end
Return foo1, foo2nd
F1, F2 = Create (1979) F1 () - print 1979f2 () f1 () - print 1989f2 () f1 () - print 1999
F1, F2 These two closing prototypes are the built-in functions foo1 and foo2 in Create, while the UpValue referenced by Foo1 and Foo2 is the same, namely the partial variable N of Create. As mentioned earlier, after the CREATE call is executed, the closure will copy the value of N on the stack, and whether F1 and F2 have a copy of N? Otherwhere, when Lua discovers UpValue in two closures, when the same variable on the current stack, it will intelligently generate a copy, then let the two closures share the copy, so that any closure to this UpValue Conducting will be explored by another. The above example clearly illustrates this: Each time the call F2 increases the value of UpValue by 10, then F1 prints out the updated value. UpValue is valuable, which allows the closure to communicate with global variables, so that the reliability of the code is greatly improved.
Closet is not in the case where it is created, and it is no longer possible to occur, because the embedded function can reference the topical variables of the outer 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 returnes
T = test (1979) F1, F2 = T () F1 () - print 1979f2 () F1 () - print 1989G1, G2 = T () G1 () - print 1989G2 () G1 () - print 1999f1 () - Print 1999
After executing t = test (1979), Test's local variable N is "dead", so when F1, F2 These two closures are created, the stack is found at all, and the traces are found at all, this is how they get n. What about it? Oh, don't forget that the Test function is not only the UpValue of Inner1 and Inner2, but it is also the UpValue of the foo. After t = test (1979), t this closure must have saved N to properly, and then F1, F2 If n is found on the current stack, it will automatically go to their outsourcing (Madoo) UpValue. Retrieve in the array and copy the found reference value to your UpValue reference array. Carefully observe the above code, you can determine that G1 and G2 are shared with the F1 and F2 to share the same UpValue. Why is this? In fact, both G1 and G2 and F1 and F2 are created by the same closure (T), so the UpValue (n) referenced by the UpValue (n) is also the same variable, and the search mechanism just described, guarantees that the last UpValue reference will be Point to the same place. Lua uses a function as a basic type value and supports the characteristics of the word legal boundary make language has powerful abstract capabilities. Thief awareness, closure and UpValue will help programmers make good use of this ability.