preface

In Lua, functions are the primary means of abstracting statements and expressions. A function can be used to perform a specific task or subroutine, or it can simply perform some calculation and return the result. In the former case, we treat a function call as a statement; In the latter case, we treat function calls as expressions.

Statement:print(8*9.9/8) expression: a =math.sin(3) + math.cos(10)
Copy the code

Note: In either case, the function call requires a pair of parentheses to enclose the argument list, even if the function being called requires no arguments, a pair of empty parentheses () is required. The only exception to this rule is when the function has only one argument and that argument is a string constant or a table constructor, the parentheses are optional

print "Hello World"             <--> print("Hello World")
dofile 'a.lua'                  <--> dofile('a.lua')
print [[a multi-line message]]  <--> print([[a multi-line message]])
f{x=10,y=20}                    <--> f({x=10,y=20})
type{}                          <--> type({})
Copy the code

Common syntax for function definitions:

Sum the elements of the sequence 'a'
function add(a)
    local sum = 0
    for i = 1, #a do
        sum = sum + a[i]
    end
    return sum
end
Copy the code

In this syntax, a function definition has a function name, a list of parameters, and a function body composed of a set of statements. The behavior of a parameter is exactly the same as that of a local variable, which is initialized with the value passed in when the function is called.

The number of arguments used to call the function may be different from the number of arguments used to define the function. The Lua language adjusts the number of arguments by dropping redundant arguments and setting insufficient arguments to nil.

Function f(a, b) print(a, b) end

f()         --> nil nil
f(3)        --> 3 nil
f(3.4)     - > 3, 4
f(3.4.5)  --> 3 4 (5 is discarded)
Copy the code

The return value more

A distinctive but useful feature in Lua is to allow a function to return multiple results, such as the string.find() function, which returns the index of the beginning and end of the string in which the pattern is matched. Functions written in Lua can also return multiple results by listing all the values to return after the return keyword.

A function that finds the largest element in a sequence can return both the maximum and the position of the element
function maximun(a)
    local mi = 1   -- Index of the maximum value
    local m = a[mi]  A maximum -
    for i = 1, #a do
        if a[i] > m then
            mi = i;
            m = a[i];
        end
    end
    return m, mi   Returns the maximum value and its index
end

Copy the code
  1. The Lua language adjusts the number of return values depending on how the function is called.
  • When a function is called as a single statement, all of its return values are discarded.
  • When a function is called as an expression, only the first return value of the function is retained.
  • When a function call is the last in a series of expressions, all of its return values are retrieved.
  1. The so-called series of expressions are represented in lua in four ways:
  • Multiple assignments
  • The list of arguments passed in when the function is called
  • Table constructor
  • Return statement

To illustrate each of these cases, here are some examples:

function foo0(a)  end                 -- Returns no result
function foo1(a)  return "a" end      -- returns 1 result
function foo2(a)  return "a"."b" end  -- Returns two results
Copy the code

In multiple assignments, if a function call is the last (or only) expression in a series of expressions, the call returns as many values as possible to match the assigned variable:

x, y = foo2()         -- x = "a",y="b"
x = foo2()            -- x = "a","b" is discarded
x, y, z = 10,foo2()   -- x = 10, y = "a", z = "b"
Copy the code

In multiple assignments, if a function does not return a value or does not return enough values, lua uses nil to replace the missing value:

x, y = foo0()     -- x = nil, y = nil
x, y = foo1()     --x = "a", y = nil
x, y, z = foo2()  -- x = "a", y = "b", z = nil
Copy the code

Note: Multivalued results can only be returned if the function call is the last (or only) expression in a series of expressions; otherwise, only one result can be returned.

x, y = foo2(), 20      X = "a", y =20 ('b' is discarded)
x, y = foo0(), 20.30  -- x = nil, y =20 (30 discarded)
Copy the code

Finally, a statement of the form return f() returns all the results returned by f:

function foo(i)
    if i == 0 then return foo0()
    elseif i == 1 then reutrn foo1()
    elseif i == 2 then return foo2()
    end
end

print(foo(1))   --> a
print(foo(2))   --> a b
print(foo(0))   --> (no result)
print(foo(3))   --> (no result)

Enclosing a function call in parentheses forces it to return only one result:
print((foo0()))   --> nil
print((foo1()))   --> a
print((foo2()))   --> a
Copy the code

You should be aware that you do not need to put parentheses after a return statement


Variable length parameter function

Functions in the Lua language can be variable-length arguments, that is, they can support a variable number of arguments. Three points in the argument list (…) When the function is called, lua internally collects all of its arguments. We call these collected arguments as extra arguments to the function. These points are still used when the function accesses these arguments, but they are used as an expression.

function add(...).
    local s = 0
    for _, v in ipairs{... }do
        s = s + v
    end
    return s
end
print(add(3.4.10.25.12))  -->  54
Copy the code

We call an expression consisting of three points a variable-length parameter expression. It behaves like a function with multiple return values, returning all the variable-length arguments to the current function. You can actually simulate lua’s common parameter passing mechanism with the side length parameter, for example:

function foo(a, b, c)
-- can be written as:
function foo(...).
    local a, b, c = ...
Copy the code

When variable-length arguments contain invalid nil, then {… } the obtained table may no longer be a valid sequence. In this case, you can use the lua provided function table.pack, which acts like the expression {… } and return them in a table with an extra field “n” that holds the number of arguments

-- Use the function table.pack to detect if nil is present in an argument
function nonils(...).
    local arg = table.pack(...)
    for i = 1.arg.n do
        if arg[i] == nil then return false end
    end
    return true
end

print(nonils(2.3.nil))  --> false
print(nonils(2.3))       --> true
print(nonils())           --> true
print(nonils(nil))        --> false
Copy the code

Another way to iterate over the variable-length arguments of a function is to use the function select(n,…) Select always takes a fixed number of selectors and a variable number of arguments. If the selector is n, then select returns all arguments after the NTH argument (including n). Otherwise, the selector should be the string “#”, which returns the total number of additional arguments.

print(select(1."a"."b"."c"))  --> a b c
print(select(2."a"."b"."c"))  --> b c
print(select(3."a"."b"."c"))  --> c
print(select(#, "a"."b"."c"))  -- > 3
Copy the code

The function table. Unpack

As the name suggests, the function table.unpack is the opposite of the function table.pack. Pcak converts an argument list into a real list in Lua, and unpack converts an actual list in Lua into a set of return values that can be used as arguments to another function.

print(table.unpack{10.20.30})   -- > 10 20 to 30
a, b = table.unpack({10.20.30})   --> a = 10, b = 20, 30 is discarded
Copy the code

Correct tail call

The Lua language supports tail call elimination, which means that Lua can correctly tail recursion, although the concept of tail call elimination does not directly address recursion.

A tail call is a jump that is used as a function call. A tail call occurs when the last action of a function is to call another function without doing any more work. Such as:

function f(x) x = x + 1; return g(x) end

When function f has called function g, it does no more work. This way, when the called function finishes executing, the program no longer needs to return the original caller. Therefore, after the final call, the program does not need to keep any information about the calling function in the call stack. When g returns, the program’s execution path returns directly to where f was called. Some language implementations, such as the Lua language interpreter, take advantage of this feature to make tail calls without using any extra stack space. We refer to this implementation as tail-call elimination.

One of the key points about tail call elimination is how to determine whether a call is a tail call. Many function calls are not tailed because they do some work after they are called. For example, the g call in the following example is not tailed:

function f(x) g(x) end

The problem with this example is that after calling g, f still has to discard the result returned by G before returning. Similarly, the following instances do not fit the definition of a tail call:

return g(x) + 1   -- Must add
return x or g(x)  You must limit the return value to one
return (g(x))     You must limit the return value to one
Copy the code