Lua-depth function (closure, non-global function, tail call)

More on Functions

The Lua function value is a first class with scoping words (lexical scoping) of the (first-class values). The first value refers to: in Lua functions and other values ​​(numeric, string), the function may be stored in a variable, or may be stored in the table, as a function of the parameters, may also function as a return value.

The word refers to the legal profession: the nested function can access variables outside of his function. This feature gives Lua provides a powerful programming capabilities.

Lua, a function of a little hard to understand the function can no name, anonymous. When we refer to function names (such as print), said variable is actually a pointer to a function, like other types of variables hold the same values:

a = {p = print} 
a.p("Hello World") --> Hello World 
print = math.sin -- `print' now refers to the sine function 
a.p(print(1)) --> 0.841470 
sin = a.p -- `sin' now refers to the print function 
sin(10, 20) --> 10 20 

Since the function is the value, then the expression can also create a function, Lua, we often write:

function foo (x) return 2*x end

This is actually the use of Lua provided "syntactic sugar" (syntactic sugar) results, the following is the original function:

foo = function (x) return 2*x end

Function definition is actually an assignment statement, the function is assigned to a variable type of variable. We use the function (x) ... end to define a function to create and use {} as a table.

a sorting table standard library function, accepts a sort table element of the table as input parameters and. This function must be able to value (string or number) of different types are sorted in ascending or descending order. Lua not provide as much parameters to meet the needs of these cases, but a sort function accepts as a parameter (similar to the C ++ function object), sort the sort function accepts two elements as input parameters, and returns the size relationship between the two, E.g:

network = { 
 {name = "grauna", IP = "210.26.30.34"}, 
 {name = "arraial", IP = "210.26.30.23"}, 
 {name = "lua", IP = "210.26.23.12"}, 
 {name = "derain", IP = "210.26.23.20"}, 
} 

If we want to sort through a list of domain name:

table.sort(network, function (a,b) 
return (a.name > b.name) 
end) 

Function as a function of other parameters are referred to in Lua advanced functions, and higher-level functions in Lua not privileged, but as a function of the first kind Lua a simple function of the result of processing.
Here's an example of drawing functions:

function eraseTerminal() 
 io.write("\27[2J") 
end 
-- writes an `*' at column `x' , row `y' 
function mark (x,y) 
 io.write(string.format("\27[%d;%dH*", y, x)) 
end 
-- Terminal size 
TermSize = {w = 80, h = 24} 
-- plot a function 
-- (assume that domain and image are in the range [-1,1]) 
function plot (f) 
 eraseTerminal() 
for i=1,TermSize.w do
 local x = (i/TermSize.w)*2 - 1 
 local y = (f(x) + 1)/2 * TermSize.h 
 mark(i, y) 
end 
 io.read() -- wait before spoiling the screen 
end 

To get this example to run correctly, you must adjust your terminal type and code control characters consistent:

plot(function (x) return math.sin(x*2*math.pi) end) 

A sinusoidal output on the screen.
The first class values in the table are the application function key Lua implements object-oriented and packet mechanisms, this section describes in a later section.

Closure

When a function is nested inside another function definition, function body can access the local variables inside a function of external, we call such a feature word scoping. While this seems clear, is not the case, the legal profession plus the word first class functions in the programming language is a powerful concept, language rarely provide such support.

Let's look at a simple example, suppose there is a list of student names and student names and scores a corresponding table;

Now think of the students sorted from high to low student achievement, you can do this:

names = {"Peter", "Paul", "Mary"} 
grades = {Mary = 10, Paul = 7, Peter = 8} 
table.sort(names, function (n1, n2) 
return grades[n1] > grades[n2] -- compare the grades 
end) 
假定创建一个函数实现此功能:
function sortbygrade (names, grades) 
 table.sort(names, function (n1, n2) 
 return grades[n1] > grades[n2] -- compare the grades 
end) 
end 

Examples contained in the interior of the sort function sortbygrade anonymous functions can access parameters sortbygrade grades, grades than in the anonymous function inside the global variable is not local variables, local variables we call the external (external local variable) or upvalue. (Upvalue meaning is somewhat misleading, however, his presence in Lua has its roots in history, and his brief compared to external local variable).
Look at the following code:

function newCounter() 
local i = 0 
return function() -- anonymous function 
 i = i + 1 
 return i 
end 
end 
c1 = newCounter() 
print(c1()) --> 1 
print(c1()) --> 2 

Anonymous function uses upvalue i save his count, when we call the anonymous function i is beyond the scope, because i created a function newCounter have returned. However, with the idea Lua closures handle this situation correctly. Simply put, the closure is a function plus upvalues ​​it can correctly access. If we call newCounter again, we will create a new local variable i, so we got a new role on the new closure variable i.

c2 = newCounter() 
print(c2()) --> 1 
print(c1()) --> 3 
print(c2()) --> 2 

c1, c2 is based on the same function, but acting in different instances of the same local variables two different closures.

Technically speaking, the closure means the value does not refer to function, the function is just a prototype declaration closure; nevertheless, in the case does not lead to confusion we continue to use the term functions on behalf of that closure.

Closures in the context of providing useful functions, such as may be seen in front of our advanced function parameters (Sort); and as a function of nested function (newCounter). This mechanism allows us a fantastic combination of programming in Lua's function in the world. Closures can also be used in the callback function, such as in a GUI environment you need to create a series of button, but the callback function is called when the user presses the button, the task may require different treatment when the button is pressed a little different.

Specifically, a decimal calculator requires 10 similar buttons, each corresponding to a digit, you can create them using the following functions:

function digitButton (digit) 
return Button{ label = digit, 
 action = function () 
 add_to_display(digit) 
 end 
 } 
end 

This example, we assume that Button is a tool used to create a new button, label is the button label, action callback function is called when the button is pressed. (Actually a closure, because he visited upvalue digit).

After the completion of the task digitButton return, local variable digit out of range, the callback function may be called and you can still access local variable digit.

Closure completely different context use is also useful. Because the function is stored in an ordinary variable we can easily re-defined or predefined function. Usually when you need the original function has a new implementation can redefine function. For example, you can redefine sin to accept a degree rather than radians as an argument:

oldSin = math.sin 
math.sin = function (x) 
return oldSin(x*math.pi/180) 
end 
更清楚的方式:

do 
local oldSin = math.sin 
local k = math.pi/180 
 math.sin = function (x) 
 return oldSin(x*k) 
end 
end 

So we put the original version placed in a local variable, the only way to access sin is the new version of the function.

Using the same characteristics that we can create a safe environment (also known as the sandbox, and java in the same sandbox), when the code (such as access to running on our network server code) we run for a period of time do not trust the security environment is needed, for example, we can use a closure redefinition file io open the library to limit the programs that are open.

do 
local oldOpen = io.open 
 io.open = function (filename, mode) 
 if access_OK(filename, mode) then
 return oldOpen(filename, mode) 
 else 
 return nil, "access denied"
 end 
end 
end 

Non-Global Functions

Lua as a function of global variables can be used as local variables, we have seen some examples: function as a table of domain (most of the Lua standard library uses this mechanism to achieve such io.read, math.sin). In this case, we must pay attention to function and table syntax:

1. 表和函数放在一起
Lib = {} 
Lib.foo = function (x,y) return x + y end
Lib.goo = function (x,y) return x - y end
2. 使用表构造函数
Lib = { 
 foo = function (x,y) return x + y end, 
 goo = function (x,y) return x - y end
} 
3. Lua 提供另一种语法方式
Lib = {} 
function Lib.foo (x,y) 
return x + y 
end 
function Lib.goo (x,y) 
return x - y 
end 

When we function is stored in a local variable, we get a local function, that is as effective as local variables within a certain range of local functions. This is defined in the package is very useful: Because the chunk as a Lua function processing, can be declared in a chunk local function (only visible in the chunk), the word legal profession to ensure that the other functions in the package can call this function. Here are two ways to declare a local function:

1. 方式一
local f = function (...) 
 ... 
end 
local g = function (...) 
 ... 
 f() -- external local `f' is visible here 
 ... 
end 
2. 方式二
local function f (...) 
 ... 
end 

One thing to note is that in the statement the local recursive function of ways:

local fact = function (n) 
if n == 0 then 
 return 1 
else 
 return n*fact(n-1) -- buggy 
end 
end 
上面这种方式导致 Lua 编译时遇到 fact(n-1)并不知道他是局部函数 fact,Lua 会去查找是否有这样的全局函数 fact。为了解决这个问题我们必须在定义函数以前先声明:
local fact 
fact = function (n) 
if n == 0 then 
 return 1 
else 
 return n*fact(n-1) 
end 
end 

In fact this internal fact (n-1) is a local call to a function call, a fact can obtain the correct value of the operation time.
But
is he Lua extended syntax allows the use of two ways when a recursive function can be defined directly.

When defining indirect recursive function must first declare a local definitions can then:

local f, g -- `forward' declarations 
function g () 
 ... f() ... 
end 
function f () 
 ... g() ... 
end 

Proper tail calls (Proper Tail Calls)

Another interesting feature is in Lua function correctly tail calling process (proper tail recursion, some books the term "tail recursion", although not related to the concept of recursion).

Last call is at a similar goto the end of the function call, when the last action is to call another function when a function, we say the end of this call is made. E.g:

function f(x) 
return g(x) 
end 

G is calling tail calls.

Not do anything after the call case f g, this case does not need to return to the caller f When the called function G; So, after the end of the caller need to keep any information about the caller in the stack . Some compilers, such as the Lua interpreter to use this feature without additional stack when dealing with a tail call, we call this the correct language support tail call.

Since the tail call does not require the use of stack space, then the tail call recursion level can be unlimited. For example, the following call no matter what the value of n does not cause a stack overflow.

function foo (n) 
if n > 0 then return foo(n - 1) end
end 

Note that: You must be clear what is the tail call.
Some callers after the function call other functions did not do other things, but not a tail call. such as:

function f (x) 
 g(x) 
return 
end 

In the above example the call g f, g return value to be discarded, so it is not tail call, the same
following examples nor tail call when:

return g(x) + 1 -- must do the addition 
return x or g(x) -- must adjust to 1 result 
return (g(x)) -- must adjust to 1 result 

Similar call Lua return g (...) is a tail call this format. But g and g parameters can be complex expressions, because Lua value of the expression will be calculated before the call. For example, the following call is tail calls:

return x[i].foo(x[j] + a*b, i + j) 

May be understood as a tail call goto, is useful in the call state machine programmed tail field. Application of the state machine requires a function to remember each state, the state changes only goto (or call) a particular function. We consider a maze game as an example: there are many maze rooms, each room has four doors East and West, each step in the direction of a movement input, and if that is the direction in which the direction of arrival of the corresponding room, otherwise the program prints a warning message.

The goal is: to reach the purpose of the room from the room began.
The maze game is a typical state machine, each room is a current status. We can write a function to achieve this maze game for each room, we use tail calls to move from one room to another room. A four room maze code is as follows:

function room1 () 
local move = io.read() 
if move == "south" then
 return room3() 
elseif move == "east" then
 return room2() 
Programming in Lua 38
Copyright ® 2005, Translation Team, www.luachina.net 
else 
 print("invalid move") 
 return room1() -- stay in the same room 
end 
end 
function room2 () 
local move = io.read() 
if move == "south" then
 return room4() 
elseif move == "west" then
 return room1() 
else 
 print("invalid move") 
 return room2() 
end 
end 
function room3 () 
local move = io.read() 
if move == "north" then
 return room1() 
elseif move == "east" then
 return room4() 
else 
 print("invalid move") 
 return room3() 
end 
end 
function room4 () 
 print("congratilations!") 
end 

We can call room1 () to start the game.
Without proper tail call, every movement must create a stack, after moving several times can lead to stack overflow.
But the right tail tail calls unlimited calling, because each time only a tail call to another function goto is not a traditional function call.

Published 252 original articles · won praise 151 · views 10000 +

Guess you like

Origin blog.csdn.net/qq_39885372/article/details/104322343