Functions are the main mechanism for abstraction of statements and expressions in Lua. Functions can both carry out a specific task (what is sometimes called a procedure or a subroutine in other languages) or compute and return values. In the first case, we use a function call as a statement; in the second case, we use it as an expression:
print(8*9, 9/8) a = math.sin(3) + math.cos(10) print(os.date())
In both cases,
a list of arguments enclosed in parentheses denotes the call;
if the call has no arguments,
we still must write an empty list ()
to denote it.
There is a special case to this rule:
if the function has one single argument
and that argument is either a literal string or
a table constructor,
then the parentheses are optional:
print "Hello World" <--> print("Hello World") dofile 'a.lua' <--> dofile ('a.lua') print [[a multi-line <--> print([[a multi-line message]] message]]) f{x=10, y=20} <--> f({x=10, y=20}) type{} <--> type({})
Lua also offers a special syntax for object-oriented calls,
the colon operator.
An expression like o:foo(x)
calls the method foo
in the object o
.
In Chapter 21, Object-Oriented Programming, we will discuss such calls
and object-oriented programming in more detail.
A Lua program can use functions defined both in Lua and in C (or in any other language used by the host application). Typically, we resort to C functions both to achieve better performance and to access facilities not easily accessible directly from Lua, such as operating-system facilities. As an example, all functions from the standard Lua libraries are written in C. However, when calling a function, there is no difference between functions defined in Lua and functions defined in C.
As we saw in other examples, a function definition in Lua has a conventional syntax, like here:
-- add the elements of sequence 'a' function add (a) local sum = 0 for i = 1, #a do sum = sum + a[i] end return sum end
In this syntax, a function definition has a name
(add
, in the example),
a list of parameters,
and a body, which is a list of statements.
Parameters work exactly as local variables
initialized with the values of the arguments
passed in the function call.
We can call a function with a number of arguments different from its number of parameters. Lua adjusts the number of arguments to the number of parameters by throwing away extra arguments and supplying nils to extra parameters. For instance, consider the next function:
function f (a, b) print(a, b) end
It has the following behavior:
f() --> nil nil f(3) --> 3 nil f(3, 4) --> 3 4 f(3, 4, 5) --> 3 4 (5 is discarded)
Although this behavior can lead to programming errors (easily spotted with minimal tests), it is also useful, especially for default arguments. As an example, consider the following function, to increment a global counter:
function incCount (n) n = n or 1 globalCounter = globalCounter + n end
This function has 1 as its default argument;
the call incCount()
, without arguments,
increments globalCounter
by one.
When we call incCount()
,
Lua first initializes the parameter n
with nil;
the or expression results in its second operand and,
as a result, Lua assigns a default 1 to n
.
An unconventional but quite convenient feature of Lua
is that functions can return multiple results.
Several predefined functions in Lua return multiple values.
We have already seen the function string.find
,
which locates a pattern in a string.
This function returns two indices when it finds the pattern:
the index of the character where the match starts
and the one where it ends.
A multiple assignment allows the program to get both results:
s, e = string.find("hello Lua users", "Lua") print(s, e) --> 7 9
(Remember that the first character of a string has index 1.)
Functions that we write in Lua also can return multiple results, by listing them all after the return keyword. For instance, a function to find the maximum element in a sequence can return both the maximum value and its location:
function maximum (a) local mi = 1 -- index of the maximum value local m = a[mi] -- maximum value for i = 1, #a do if a[i] > m then mi = i; m = a[i] end end return m, mi -- return the maximum and its index end print(maximum({8,10,23,12,5})) --> 23 3
Lua always adjusts the number of results from a function to the circumstances of the call. When we call a function as a statement, Lua discards all results from the function. When we use a call as an expression (e.g., the operand of an addition), Lua keeps only the first result. We get all results only when the call is the last (or the only) expression in a list of expressions. These lists appear in four constructions in Lua: multiple assignments, arguments to function calls, table constructors, and return statements. To illustrate all these cases, we will assume the following definitions for the next examples:
function foo0 () end -- returns no results function foo1 () return "a" end -- returns 1 result function foo2 () return "a", "b" end -- returns 2 results
In a multiple assignment, a function call as the last (or only) expression produces as many results as needed to match the variables:
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"
In a multiple assignment, if a function has fewer results than we need, Lua produces nils for the missing values:
x,y = foo0() -- x=nil, y=nil x,y = foo1() -- x="a", y=nil x,y,z = foo2() -- x="a", y="b", z=nil
Remember that multiple results only happen when the call is the last (or only) expression in a list. A function call that is not the last element in the list always produces exactly one result:
x,y = foo2(), 20 -- x="a", y=20 ('b' discarded) x,y = foo0(), 20, 30 -- x=nil, y=20 (30 is discarded)
When a function call is the last (or the only)
argument to another call,
all results from the first call go as arguments.
We saw examples of this construction already,
with print
.
Because print
can receive a variable number of arguments,
the statement print(g())
prints all results returned by g
.
print(foo0()) --> (no results) print(foo1()) --> a print(foo2()) --> a b print(foo2(), 1) --> a 1 print(foo2() .. "x") --> ax (see next)
When the call to foo2
appears inside an expression,
Lua adjusts the number of results to one;
so, in the last line,
the concatenation uses only the first result, "a"
.
If we write f(g())
,
and f
has a fixed number of parameters,
Lua adjusts the number of results from g
to the number
of parameters of f
.
Not by chance,
this is exactly the same behavior that happens in
a multiple assignment.
A constructor also collects all results from a call, without any adjustments:
t = {foo0()} -- t = {} (an empty table) t = {foo1()} -- t = {"a"} t = {foo2()} -- t = {"a", "b"}
As always, this behavior happens only when the call is the last expression in the list; calls in any other position produce exactly one result:
t = {foo0(), foo2(), 4} -- t[1] = nil, t[2] = "a", t[3] = 4
Finally, a statement like return f()
returns all values
returned by f
:
function foo (i) if i == 0 then return foo0() elseif i == 1 then return foo1() elseif i == 2 then return foo2() end end print(foo(1)) --> a print(foo(2)) --> a b print(foo(0)) -- (no results) print(foo(3)) -- (no results)
We can force a call to return exactly one result by enclosing it in an extra pair of parentheses:
print((foo0())) --> nil print((foo1())) --> a print((foo2())) --> a
Beware that a return statement does not need parentheses around
the returned value;
any pair of parentheses placed there counts as an extra pair.
Therefore,
a statement like return (f(x))
always returns one single value,
no matter how many values f
returns.
Sometimes this is what we want, sometimes not.
A function in Lua can be variadic,
that is,
it can take a variable number of arguments.
For instance, we have already called print
with one, two, and more arguments.
Although print
is defined in C,
we can define variadic functions in Lua,
too.
As a simple example, the following function returns the summation of all its arguments:
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
The three dots (...
) in the parameter list indicate that
the function is variadic.
When we call this function,
Lua collects all its arguments internally;
we call these collected arguments the extra arguments
of the function.
A function accesses its extra arguments using again the three dots,
now as an expression.
In our example,
the expression {...}
results in a list
with all collected arguments.
The function then traverses the list to add its elements.
We call the three-dot expression
a vararg expression.
It behaves like a multiple return function,
returning all extra arguments of the current function.
For instance, the command print(...)
prints all extra arguments of the function.
Likewise,
the next command creates two local variables
with the values of the first two
optional arguments (or nil if there are no such arguments):
local a, b = ...
Actually, we can emulate the usual parameter-passing mechanism of Lua translating
function foo (a, b, c)
to
function foo (...) local a, b, c = ...
Those who fancy Perl’s parameter-passing mechanism may enjoy this second form.
A function like the next one simply returns all its arguments:
function id (...) return ... end
It is a multi-value identity function.
The next function behaves exactly like another function foo
,
except that before the call
it prints a message with its arguments:
function foo1 (...) print("calling foo:", ...) return foo(...) end
This is a useful trick for tracing calls to a specific function.
Let us see another useful example.
Lua provides separate functions for formatting text (string.format
)
and for writing text (io.write
).
It is straightforward to combine both functions into a single
variadic function:
function fwrite (fmt, ...) return io.write(string.format(fmt, ...)) end
Note the presence of a fixed parameter
fmt
before the dots.
Variadic functions can have any number of fixed parameters
before the variadic part.
Lua assigns the first arguments to these parameters;
the rest (if any) goes as extra arguments.
To iterate over its extra arguments,
a function can use the expression {...}
to collect them all in a table,
as we did in our definition of add
.
However,
in the rare occasions when the extra arguments
can be valid nils,
the table created with {...}
may not be a proper sequence.
For instance, there is no way to detect in such a table
whether there were trailing nils in the original arguments.
For these occasions,
Lua offers the function table.pack
.[8]
This function receives any number of arguments and
returns a new table with all its arguments
(just like {...}
),
but this table has also an extra field "n"
,
with the total number of arguments.
As an example,
the following function uses table.pack
to test whether none of its arguments is nil:
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
Another option to traverse the variable arguments of
a function is the select
function.
A call to select
has always one fixed argument,
the selector,
plus a variable number of extra arguments.
If the selector is a number n
,
select
returns all arguments after the n
-th argument;
otherwise, the selector should be the string "#"
,
so that select
returns the total number of extra 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
More often than not,
we use select
in places where its number of results
is adjusted to one,
so we can think about select(n, ...)
as returning
its n
-th extra argument.
As a typical example of the use of select
,
here is our previous add
function using it:
function add (...) local s = 0 for i = 1, select("#", ...) do s = s + select(i, ...) end return s end
For few arguments,
this second version of add
is faster,
because it avoids the creation of a new table at each call.
For more arguments, however,
the cost of multiple calls to select
with many arguments
outperforms the cost of creating a table,
so the first version becomes a better choice.
(In particular, the second version has a quadratic cost,
because both the number of iterations and the number of
arguments passed in each iteration grow with the number
of arguments.)
A special function with multiple returns is table.unpack
.
It takes a list
and returns as results all elements from the list:
print(table.unpack{10,20,30}) --> 10 20 30 a,b = table.unpack{10,20,30} -- a=10, b=20, 30 is discarded
As the name implies,
table.unpack
is the reverse of table.pack
.
While pack
transforms a parameter list into
a real Lua list (a table),
unpack
transforms a real Lua list (a table)
into a return list,
which can be given as
the parameter list to another function.
An important use for unpack
is in a generic call mechanism.
A generic call mechanism allows us to call any function,
with any arguments, dynamically.
In ISO C, for instance,
there is no way to code a generic call.
We can declare a function
that takes a variable number of arguments (with stdarg.h
)
and we can call a variable function, using pointers to functions.
However,
we cannot call a function with a variable number of arguments:
each call you write in C has a fixed number of arguments,
and each argument has a fixed type.
In Lua, if we want to call a variable function f
with variable arguments in an array a
,
we simply write this:
f(table.unpack(a))
The call to unpack
returns all values in a
,
which become the arguments to f
.
For instance, consider the following call:
print(string.find("hello", "ll"))
We can dynamically build an equivalent call with the following code:
f = string.find a = {"hello", "ll"} print(f(table.unpack(a)))
Usually, table.unpack
uses the length operator to know how
many elements to return, so it works only on proper sequences.
If needed, however, we can provide explicit limits:
print(table.unpack({"Sun", "Mon", "Tue", "Wed"}, 2, 3)) --> Mon Tue
Although the predefined function unpack
is written in C,
we could write it also in Lua,
using recursion:
function unpack (t, i, n) i = i or 1 n = n or #t if i <= n then return t[i], unpack(t, i + 1, n) end end
The first time we call it,
with a single argument, the parameter i
gets 1
and n
gets the length of the sequence.
Then the function returns t[1]
followed by all results from unpack(t, 2, n)
,
which in turn returns t[2]
followed by all results from unpack(t, 3, n)
,
and so on, stopping after n
elements.
Another interesting feature of functions in Lua is that Lua does tail-call elimination. (This means that Lua is properly tail recursive, although the concept does not involve recursion directly; see Exercise 6.6.)
A tail call is a goto dressed as a call.
A tail call happens when a function calls another
as its last action,
so it has nothing else to do.
For instance, in the following code,
the call to g
is a tail call:
function f (x) x = x + 1; return g(x) end
After f
calls g
, it has nothing else to do.
In such situations,
the program does not need to return to the calling function
when the called function ends.
Therefore, after the tail call,
the program does not need to keep any information
about the calling function on the stack.
When g
returns,
control can return directly to the point that called f
.
Some language implementations, such as the Lua interpreter,
take advantage of this fact and actually do not use any extra
stack space when doing a tail call.
We say that these implementations do tail-call elimination.
Because tail calls use no stack space, the number of nested tail calls that a program can make is unlimited. For instance, we can call the following function passing any number as argument:
function foo (n) if n > 0 then return foo(n - 1) end end
It will never overflow the stack.
A subtle point about tail-call elimination is what is a tail call.
Some apparently obvious candidates
fail the criterion that the calling function
has nothing else to do after the call.
For instance, in the following code,
the call to g
is not a tail call:
function f (x) g(x) end
The problem in this example is that,
after calling g
,
f
still has to discard
any results from g
before returning.
Similarly, all the following calls fail the criterion:
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
In Lua,
only a call with the form
return
is a tail call.
However,
both func
(args
)func
and its arguments can be complex expressions,
because Lua evaluates them before the call.
For instance, the next call is a tail call:
return x[i].foo(x[j] + a*b, i + j)
Exercise 6.1: Write a function that takes an array and prints all its elements.
Exercise 6.2: Write a function that takes an arbitrary number of values and returns all of them, except the first one.
Exercise 6.3: Write a function that takes an arbitrary number of values and returns all of them, except the last one.
Exercise 6.4: Write a function to shuffle a given list. Make sure that all permutations are equally probable.
Exercise 6.5: Write a function that takes an array and prints all combinations of the elements in the array. (Hint: you can use the recursive formula for combination: C(n,m) = C(n -1, m -1) + C(n - 1, m). To generate all C(n,m) combinations of n elements in groups of size m, you first add the first element to the result and then generate all C(n - 1, m - 1) combinations of the remaining elements in the remaining slots; then you remove the first element from the result and then generate all C(n - 1, m) combinations of the remaining elements in the free slots. When n is smaller than m, there are no combinations. When m is zero, there is only one combination, which uses no elements.)
Exercise 6.6: Sometimes, a language with proper-tail calls is called properly tail recursive, with the argument that this property is relevant only when we have recursive calls. (Without recursive calls, the maximum call depth of a program would be statically fixed.)
Show that this argument does not hold in a dynamic language like Lua: write a program that performs an unbounded call chain without recursion. (Hint: see the section called “Compilation”.)
Personal copy of Eric Taylor <jdslkgjf.iapgjflksfg@yandex.com>