在 Lua中,函数是一种对语句和表达式进行抽象的主要机制。函数既可以完成某项特定的任务,也可以只做一些计算并返回结果。
若一个函数只有一个参数,并且此参数是一个字面字符串或table构造式,那么圆括号可有可无。
例如:
print "hello world'
dofile 'a.lua'
print [[a multi-line message]]
f{x = 10, y=20}
type{}
Lua为面向对象式的调用也提供了一种特殊的语法-冒号操作符。表达式o.foo(o,x)的另一种写法是o:foo(x),冒号操作符使用o.foo时隐含地作为函数的第一个参数。
Lua具有一项非常与众不同的特征,允许函数返回多个结果。只需要在 return 关键字后列出所有的返回值即可。
例如:
function maximum(a)
local mi = 1 --最大值索引
local m = a[mi] --最大值
for i, val in ipairs(a) do
if val > m then
m = val
end
end
return m, mi
end
print( maximum({8,10,23,12,5})) -- 23 2
Lua会调整一个函数的返回值数量以适应不同的调用的情况。若函数做为一条单独语句时,Lua会丢弃函数的所有返回值。若将函数以表达式的一部分来调用,Lua只保留函数
的第一个返回值。只有当一个函数调用是一系列表达式中的最后一个元素(或仅有一个元素)时 ,才能获得它的所有返回值。
"一系列表达式"在Lua中表现为4种情况:多重赋值、函数调用时传入的实参列表、table的构造式和return 语句。
例如:
function foo0() end--无返回值
function foo1() return "a" end--返回1个结果
function foo2() return "a","b" end --返回2个结果
在多重赋值中,若一个函数调用是最后(或仅有的)一个表达式,那么Lua会保留尽可能多的返回值,用于匹配赋值变量:
x,y = foo2() --x="a", y ="b"
x = foo2() --x="a","b"被丢弃
x,y,z=10,foo2() -- x=10,y="a",z="b"
如果一个函数没有返回值或者返回没有足够多的返回值,那么Lua会用nil来补充缺失的值:
x,y = foo0() --x=nil,y=nil
x,y = foo1() --x="a",y=nil
x,y,z = fool2() --x="a",y="b",z=nil
如果一个函数调用不是一系列表达式的最后一个元素,那么将只产生一个值:
x,y =foo2(),20 -- x="a",y =20
x,y= foo0(),20,30 --x=nil,y=20, "30"被丢弃
当一个函数调用作为另一个函数调用的最后一个(或仅有的)实参时,第一个函数的所有返回值都将作为实参传入第二个函数。
例如:
pirnt( foo0() )
print( foo1() ) --a
print( fool2() ) --a b
print(foo2(),1) --a 1
print( foo2() .. "x") --ax
table构造式可以完整的接收一个函数调用的所有结果,即不会有任何数量方面的调整:
t = { foo0() } -- t = {}(一个空的table)
t = { foo1() } --t={"a"}
t = { foo2() } --t={ "a","b" }
这种行为只有当一个函数调用作为最后一个元素时才会发生,而在其他位置的函数调用总是只产生一个结果值:
t = { foo0() , foo2(), 4 } --t[1] = nil, t[2] ="a", t[3] = 4
最后一种是return语句,诸如return f()这样的语句将返回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)) -- 无返回值
print( foo(3) ) --无返回值
可以将一个函数调用放入一对圆括号中,可以迫使它只返回一个结果:
print( ( foo2() ) ) -- a
return语句后面的内容是不需要圆括号的,在该位置上书写圆括号会导致不同的行为。
例如:
return( f(x) ),将只返回一个值,而无关乎f返回几个值。
关于多重返回值还有一个特殊函数:unpack。它接受一个数组作为参数,从下标1开始返回该数组的所有元素:
print(unpack{10,20,30}) --10,20,30
a,b = unpack{10,20,30} --a = 10, b =20 "30"被丢弃
unpack的一项重要用途体现在"泛型调用"机制中。泛型调用极致可以动态的以任何参数来调用任何函数。
变长参数
Lua中的函数还可以接受不同数量的实参。
例如:
function add( ... )
local s = 0
for i, v in ipairs{...} do
s = s+v
end
return s
end
参数表中的3个点(...)表示该函数可接受不同数量的实参。
具有变长参数的函数同样可以拥有任意数量的固定参数,但固定参数必须放在变长参数之前。
在某些特殊情况下,变长参数中可能会包含一些故意传的nil,那么此时就需要用函数select来访问变长参数了。调用select时,必须传入一个固定实参selector和一系列变长参数。如果selector为数字n,那么select返回它的第n个可变实参;否则selector只能为字符串"#",这样select会返回变长参数的总数。
例如:
for i=1, select('#', ...)
local arg = select(i, ...) -- 得到第i个参数
<循环体>
end
select('#', ... )会返回所有变长参数的总数,其中包括nil
具名实参
Lua中的参数传递机制是具有"位置性"的,也就是说在调用一个函数时,实参时通过它的参数表中的位置与形参匹配起来的。
有时候通过名称来指定实参也是很用的。Lua并不直接支持这种语法,但可以通过一种细微的改变来获得相同的效果。将素有实参组织到一个table中,并将这个table作为唯一的实参传给函数。当实参只有一个table构造时,函数调用中圆括号是可有可无的。