Learning about closure operations in LUA

Source: Internet
Author: User

AboutLUAMediumClosureThe operation learning tutorial is the content to be introduced in this article,LuaInFunctionIt is a first-class value. Defining a function is just 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 function f2 is defined inFunctionIn f1, 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:

 
 
  1. function f1(n) 

The function parameter is also a local variable.

 
 
  1. Local function f2 ()
  2. Print (n) -- reference local variables of outsourcing functions
  3. End
  4. Return f2
  5. End
  6.  
  7. G1 = f1 (0, 1979)
  8. G1 () -- print 1979
  9. G2 = f1 (500)
  10. 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 ). In 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 some debugging information.

At runtimeLuaExecute a function... when an expression such as end is used, it creates a new data object, which contains the reference and environment of the corresponding function prototype (environment, used to find the table with global variables) and an array composed of all upvalue references. This data object is calledClosure. It can be seen that the function is the concept of compile-time, static, and closure is the concept of runtime and dynamic.

The values of g1 and g2 are strictly not functionsClosureAnd are two differentClosureAnd eachClosureYou can retain your own upvalue value, so the output of g1 and g2 is certainly different. AlthoughClosureFunctions 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:

 
 
  1. Function f1 (n)
  2. Local function f2 ()
  3. Print (n)
  4. End
  5. Nn = n + 10
  6. Return f2
  7. End
  8.  
  9. G1 = f1 (0, 1979)
  10. G1 () -- print 1989

The nested function is defined before n = n + 10. Why does g1 () print 1989? Upvalue is actually a local variable, while a local variable is saved on the function stack framework (stack frame). As long as upvalue has not left its own scope, it always lives on the function stack. In this case, the closure will access them by pointing to the upvalue reference on the stack, and once upvalue is about to leave its own scope (which also means it will disappear from the stack immediately ), the closure will allocate space for it and save the current value. Then, you can access the upvalue by pointing to the reference of the newly allocated space.

When 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 return f2 is complete, n is about to end. In this case, the closure copies n (1989 already) to the space you manage 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:

 
 
  1. Function Create (n)
  2. Local function foo1 ()
  3. Print (n)
  4. End
  5. Local function foo2 ()
  6. Nn = n + 10
  7. End
  8. Return foo1, foo2
  9. End
  10. F1, f2 = Create (1979)
  11. F1 () -- prints 1979
  12. F2 ()
  13. F1 () -- prints 1989
  14. F2 ()
  15. F1 () -- prints 1999

F1 and f2ClosureThe prototype is the built-in functions foo1 and foo2 in Create, while the upvalue referenced by foo1 and foo2 are the same, that is, the local variable n in Create. 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? Otherwise, whenLuaTwoClosureWhen the upvalue points to the same variable on the current stack, only one copy is generated intelligently, and then the twoClosureShare the copy.ClosureModifications to this 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.

ClosureAt the time of creation, the upvalue is no longer on the stack. This is because the nested function can reference the local variables of the outer outsourcing function:

 
 
  1. Function Test (n)
  2. Local function foo ()
  3. Local function inner1 ()
  4. Print (n)
  5. End
  6. Local function inner2 ()
  7. Nn = n + 10
  8. End
  9. Return inner1, inner2
  10. End
  11. Return foo
  12. End
  13.  
  14. T = Test (1, 1979)
  15. F1, f2 = t ()
  16. F1 () -- prints 1979
  17. F2 ()
  18. F1 () -- prints 1989
  19. G1, g2 = t ()
  20. G1 () -- prints 1989
  21. G2 ()
  22. G1 () -- prints 1999
  23. F1 () -- prints 1999

After the execution of t = Test (1979), the local variable n of Test is "dead", so when f1, f2ClosureN is not found on the stack at the time of creation. How do they obtain n values? 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 = Test (1979), tClosureN must have been properly saved. If n cannot be found on the current stack, f1 and f2 will automatically go to their outsourcing closure (this should be called) in the upvalue reference array, and copy the reference value to your upvalue reference array.

Observe the above Code carefully to determine that g1 and g2 share the same upvalue with f1 and f2. 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 earlier ensures that all upvalue references in the end point to the same place.

Summary: AboutLUAMediumClosureThe operation learning tutorial is complete. I hope this article will help you!

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.