Preface
There is the concept of closures in many languages, and here, I will mainly analyze and summarize the concept of closures in Lua language. I hope it will be helpful for everyone to learn Lua.
What is a closure?
Closures are a very important concept in Lua. Closures are entities composed of functions and reference environments related to them. Let's look at another piece of code:
function newCounter()
local i = 0
return function () -- anonymous function
i = i + 1
return i
end
end
c1 = newCounter()
print(c1())
print(c1())
Based on the concept of closure just mentioned, combined with the above code, let’s talk about this concept. Closure = Function + Reference Environment. The newCounter function in the above code returns a function, and the returned anonymous function is the function in the component part of the closure; the reference environment is the environment where the variable i is located. In fact, closures are just like functions in form and expression, but they are not functions in fact. We all know that functions are combinations of executable statements. These code statements are determined after the function is defined and will not change when executed again, so there is only one instance of the function. The closure can have multiple instances at runtime. Different reference environments and the same function combination can produce different instances, just like the same class code that can create different class instances. When reading other people's articles, I saw that there is a saying: child functions can use local variables in parent functions, and this behavior is called closure! This statement actually shows a manifestation of closures, allowing us to better understand what closures are from an external form. As for the deep closure, we continue.
Look at the closure again
Friends who have read my blog know that my previous blogs have written about C++. For me who is studying C++, there are indeed some "difficulty" when understanding Lua's closures. First of all, in Lua, creating a function is like defining a normal type value. This is what I said in my previous blog post, there is no difference between a function and a normal type in Lua. Functions in Lua are so-called "first-class values". They can be stored in variables or data structures, passed as parameters to another function, can be the return value of a function, and can be created during runtime. Functions in Lua are such a "thing", which is very flexible. Do you still remember the concept of "non-local variables" mentioned in my blog post "Functions in Lua"? This is a very important concept. It can be understood as not a variable defined within the local scope of action. At the same time, it is not a global variable, which is what we call upvalue. Because of the existence of such a variable, it fulfills the closure in Lua. This type of variable is mainly used in nested functions and anonymous functions. We all know that functions can be defined in Lua's functions, that is, embedded functions. Inline functions can access all local variables that have been created by external functions, and these variables are called the upvalue of the embedded function. Upvalue actually refers to variables rather than values. These variables can be shared among internal functions, such as the following code:
function Fun1()
local iVal = 10 -- upvalue
function InnerFunc1() -- Inline function
print(iVal) --
end
function InnerFunc2() -- Inline function
iVal = iVal + 10
end
return InnerFunc1, InnerFunc2
end
-- Assign the function to a variable, at this time, variable a binds the function InnerFunc1, b binds the function InnerFunc2
local a, b = Fun1()
-- Call a
a() -->10
-- Call b
b() -->Modified upvalue iVal in function b
-- Call a to print the modified upvalue
a() -->20
The above simple code verifies that the upvalue is shared in embedded functions, just like a member function in a C++ class can access and modify member variables.
Use closure
You can see that closures are a combination of data and behavior, just like classes in C++, which makes closures have good abstraction capabilities. In some cases, we need to remember the state of data after a call is completed, just like static type variables in C++. After each call is completed, static type variables will not be cleared. This function can be completed well with closures. In the next blog post, I will talk about using closures to complete the iterator function.
Summarize
Closures are a very always-on concept, which is easy to understand and difficult to understand. Simply put, closures are embedded functions plus upvalues that can be accessed correctly. Many times, we understand this principle but will not use this thing. Therefore, we need to read more code, participate in more projects, accumulate more project experience, and enrich our experience. At that time, the level of understanding will improve.