Generic for
Exercise 4.5: Can you explain why Lua has the restriction that a goto cannot jump out of a function? (Hint: how would you implement that feature?)
5.1 Multiple Results
An unconventional, but quite convenient feature of Lua is that functions can return multiple results. Several predefined functions in Lua return multiple
5.1 Multiple Results 43
values. An example is thestring.find function, which locates a pattern in a string. This function returns two indices when it finds the pattern: the index of the character where the pattern 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
(Note 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 maxi-mum element in a sequence can return both the maximaxi-mum 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]
endend
return m, mi end
print(maximum({8,10,23,12,5})) --> 23 3
Lua always adjusts the number of results from a function to the circum-stances 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, 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"
If a function has no results, or not as many results as 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
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
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 have seen examples of this construction already, withprint. Because the print function can receive a variable number of arguments, the statementprint(g()) prints all results returned byg.
print(foo0()) -->
print(foo1()) --> a print(foo2()) --> a b print(foo2(), 1) --> a 1
print(foo2() .. "x") --> ax (see next)
When the call tofoo2 appears inside an expression, Lua adjusts the number of results to one; so, in the last line, the concatenation uses only the “a”.
If we writef(g()) and f has a fixed number of arguments, Lua adjusts the number of results ofg to the number of parameters of f, as we saw previously.
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 likereturn 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() endend
print(foo(1)) --> a print(foo(2)) --> a b
print(foo(0)) -- (no results) print(foo(3)) -- (no results)
You can force a call to return exactly one result by enclosing it in an extra pair of parentheses:
5.1 Multiple Results 45
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 likereturn(f(x)) always returns one single value, no matter how many valuesf returns. Sometimes this is what you want, sometimes not.
A special function with multiple returns istable.unpack. It receives an array and returns as results all elements from the array, starting from index 1:
print(table.unpack{10,20,30}) --> 10 20 30
a,b = table.unpack{10,20,30} -- a=10, b=20, 30 is discarded An important use forunpack is in a generic call mechanism. A generic call mechanism allows you to call any function, with any arguments, dynamically.
In ANSI C, for instance, there is no way to code a generic call. You can declare a function that receives a variable number of arguments (withstdarg.h) and you can call a variable function, using pointers to functions. However, you 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 you want to call a variable functionf with variable arguments in an array a, you simply write this:
f(table.unpack(a))
The call tounpack returns all values in a, which become the arguments to f. For instance, consider the following call:
print(string.find("hello", "ll"))
You can dynamically build an equivalent call with the following code:
f = string.find a = {"hello", "ll"}
print(f(table.unpack(a)))
Usually, unpack uses the length operator to know how many elements to return, so it works only on proper sequences. If needed, you can provide explicit limits to it:
print(table.unpack({"Sun", "Mon", "Tue", "Wed"}, 2, 3)) --> Mon Tue
Although the predefined unpack function 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) endend
The first time we call it, with a single argument, i gets 1 and n gets the length of the sequence. Then the function returnst[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.