Lua is a dynamic type language that can be bound to any type of value at runtime. Lua draws a lot of ideas from the functional programming language, which is reflected in the basic type of Number, String, etc. in the basic type of LUA, and has functions. This means that the function: <1> can be named with variables; <2> can be provided to the function as a parameter; <3> can be used as a return value of the function; <4> can be included in the data structure. Below we use functions as a "first-class citizen" as a "first-class citizen".
Suppose there is such two functions f: D1 × D2 × D3 × ... × DN -> R (this indicates that the defined domain of the function f is a subset of the designs of D1 to DN, and the value domain is a subset of R ), G: r -> s, the function f and g are composite, and their composite function h satisfies the following requirements, the definition domain is the same as F, and the value field is the same as G, and for each defined domain Elements E For H (e) value equal to G (E)). Our goal is to define a function that completes the composite action, and its input is any two functions that meet the comparative conditions, and the output is their composite functions. Here is a specific implementation:
Function compose1 (f, g) local newfn = function (...) local old_func = f
- @ Because Old_Func parameters are uncertain, the program text of this function calls this function - @ Using Lua's dynamic compilation function - reflective mechanism provided by the Debug module LOCAL S = "Local Name, OLD_FN OLD_FN_ARG / N "-. is Lua string connection operator s = s .." name, old_fn_arg = debug.getlocal (2, 1) / n "s = s .." Name, OLD_FN = Debug. GetLocal (2, 2) / n "s = s .." Return Old_fn ("for i, v in ipairs (arg) do s = s..tring.format (" OLD_FN_ARG [% D], ", i) end s = s .. "nil)"
Local Dynamic_Func = Assert (LoadString (s)) Return G (Dynamic_Func ()) end
Return Newfnend
Define in the compose1 function (actually created, like creating other type objects) has a new and parameter number of unsure anonymous functions, and bind it with the local variable name FN, which means FN This name is the active function, unless you bind Fn with other objects. We see that this bind is implemented by assigning operators, which is identical to the modem that binds to other basic type values. If you define a function of unsure of the number of parameters, the system will automatically package all actual parameters to a list structure when this function is called, and automatically binds the list to local variable name arg in the function body. System implicit definition). The local variable name S in the new defined function represents a string, which stores the F function call program text that is dynamically expanded according to the number of parameters passed during actual call (because the parameters of f are not sure in advance, so we can't in the program The form of calling into F (a, b) is in the form of calls). LoadString (S) The return value (bind to Dynamic_FUNC) is a function, this function is obtained by dynamically compiling the S-string, but the environment of this function (Enviroment, can be visible to the function can be accessed The collection of all global variables is set to the system's global environment, so when executing the Dynamic_FUNC function, it does not know what the variable names such as arg, old_func represents what it means (they are local variables of the newfn function, there is no ), So we insert a function call such as debug.getlocal (.) To obtain a value of local variables in the newfn function. For example, name, old_fn_arg = debug.getlocal (2, 1) is executed to get the first local variable of the second layer of the second layer function in the call stack, that is, Name has obtained the first local variable in the newfn function. The name "arg" of Arg, and OLD_FN_ARG gets its value, that is, the unproductive parameter list of the packaged newfn. The same principle Name, OLD_FN = Debug.getlocal (2, 2) acquired the name of the second local variable in the newfn ("OLD_FUNC") and the value (that is, the value passed to compose1 gin F, because in the newfn function body There is an assignment action of local ild_func = f). This allows the external transfer parameters to be accessed in dynamically compiled, which can be accessed to achieve the purpose of dynamic calls. Through the last return statement in the newfn function, the effect of each call NewFn is called in such a composite to perform G (Dynamic_Func ()), and then the Newfn function itself is transmitted as a return value. You can test whether the compose1 function can work correctly by the following code:
Function double (x) returnx x * 2nd
- Add all parameters - and return the sumfunction add (...) local sum = 0 for i, v in iPairs (arg) do sum = sum v end return SUMEND
F1 = compose1 (double, print) F2 = compose1 (add, print)
F1 (18) - Print 36
F2 (1, 2, 3, 4, 5, 6, 7, 8, 9, 10) - Print 55
The above Compose1 works well, but the resulting composite function is definitely not too high. Because each call the result function, the dynamic compilation process is performed (caused by LoadString (..), this is a considerable resource action. So is there a way to avoid dynamic compilation? In fact, it is necessary to insert LoadString in the generated composite function (.) Is because the number of F functions is uncertain, we cannot write down the call code in a fixed format in the program. For example, when you write F (Arg [1], Arg [2], ..., arg [n]), you lose the ability to handle more than N-n-n-N. Fortunately, Lua can automatically package a variable number parameter into a list, but also provides a function to restore the list to the original comma-separated form, this function is unpack. When you write unpack (arg), it is equivalent to writing Arg [1], arg [2], ..., arg [n] (n is the number of actual elements of the list), but you can only target it. A fixed constant N-write, and the Unpack function can "write" according to the actual elements in the ARG. Now we can use unpack to improve the above compose1: function compose2 (f, g) local newfn = function (...) Return G (F (unpack)) end
Return Newfnend
It can be seen that the new implementation version is very simple, which is completely worshiped, and its powerful expression capability provides great convenience for operating parameters, but also makes more complex functional operations.