在lua中,函数是一种对语句和表达式进行抽象的主要机制。函数既可以完成某项特定
的任务,也可以只做一些计算并返回结果;
lua为面向对象式的调用也提供了一种特殊语法——冒号操作符。表达式o.foo(o,x)的
另一种写法是o:foo(x),冒号操作符使调用o.foo时将o隐含地作为函数的第一个参数;
一个lua程序既可以使用lua编写的函数,又可以调用以C语言编写的函数。
所有lua标准程序库中的函数都是用C语言写的;
函数中形式参数的使用方式与局部变量相似,它们是由调用函数时的实际参数初始化
的。调用函数时提供的实参数量可以与形参数量不同,lua会自动调整实参的数量以
匹配参数表的要求,即"若实参多于形参,则舍弃多余的实参;若实参不足,则多余
的形参初始化为nil";但这种行为可能会导致一些编程错误,尽量不要去这么做;
5.1多重返回值
lua具有一项非常与众不同的特征,即允许函数返回多个结果,只需在return关键字
后列出所有的返回值即可;
lua也会调整一个函数的返回值数量以适应不同的调用情况;
只有当一个函数调用时一系列表达式中的最后一个元素时,才能获得它的所有返回值;
这里的“一系列表达式”表现为4中情况:多重赋值,函数调用时传入的实参列表,table
的构造式 和 return语句;
在多重赋值中:
如果一个函数调用时最后一个表达式,那么lua会保留其尽可能多的返回值,用于
匹配赋值变量;
如果一个函数没有返回值或者没有足够多的返回值,那么lua会用nil来补充缺失的值;
如果一个函数调用不是一系列表达式的最后一个元素,那么只将产生一个值;
如果一个函数调用作为另一个函数调用的最后一个实参时,第一个函数的所有返回值
都将作为实参传入第二个函数,若第一个函数出现在一个表达式中时,lua会将其返回
值数量调整为1;例子 ——> print(foo2() .."x")
table构造式可以完整地接收一个函数调用的所有结果,即不会有数量方面的调整;
不过这种行为只有当函数调用作为table的最后一个元素时才会发生,而在其他位置
上的函数调用总是只产生一个结果值;
如果一个函数调用时作为return语句的最后一个元素时会返回它的所有结果;
可以将一个函数调用放入一对圆括号中迫使它只返回一个结果;
一个特殊的函数 —— unpack,它接受一个数组作为参数,并从下标1开始返回该数组的所有
元素;unpack的一项重要用途体现在“泛型调用--genericcall”机制中;
泛型调用机制可以动态地以任何实参来调用任何函数;如果想调用任意函数f,而所有的参数
都在数组a中,那么可以这么写:
f(unpack(a))
unpack 将返回a中所有的值,这些值将作为函数f的实参。
unpack的lua实现(通过递归):
function unpack(t, i)
i = i or 1
if t[i] then
returnt[i], unpack(t, i+1)
end
end
5.2变长参数
lua函数可以接受不同数量的实参;例如:
function add(...)
local s = 0
for i, v inipairs{...} do
s = s + v
end
return s
end
参数表中的3个点(...)表示函数可接受不同数量的实参。当函数被调用时,它的所有参数会被收集到一起,
称为这个函数的"变长参数";
一个函数要访问它的参数时仍需要3个点(...),此时这3个点是作为一个表达式来使用的。
表达式"..."的行为类似于一个具有多重返回值的函数,它返回的是当前函数的所有变长参数;
例如: local a, b, c = ...
具有变长参数的函数同样也可以拥有任意数量的固定参数,但固定参数必须放在变长参数之前。
遍历一个函数的变长参数时只需要使用表达式{...},这就像访问一个table一样;
但在某些特殊情况下,变长参数会包含一些故意传入的nil,那么此时就需要用函数select来访问变长参数了。
调用select时,必须传入一个固定实参selector(选择开关) 和 一系列变长参数。如果selector为数字n,那么它
返回它的第n个可变实参;否则,selector只能为字符串"#",这样select会返回变长参数的总数,其中包括nil;
例如:
for i=1, select("#", ...) do
local arg =select(i, ...)
<循环体>
end
了解Lua5.0 与 Lua5.1 关于变长参数的不同!
5.3具名实参
Lua的参数传递机制是具有位置性的,也就是说在调用一个函数时实参是通过它在参数表中的位置与形参
匹配起来的。但有时通过名称来指定实参也是很有用的。具体做法:将所有实参组织到一个table中,
并将这个table作为唯一的实参传给函数。如:
function rename(arg)
returnos.rename(arg.old,arg.new)
end
函数rename的参数改为只接受一个table类型的参数;在调用rename时,要注意Lua中特殊的函数调用语法,
就是当实参只有一个table构造式时,函数调用中的圆括号是可有可无的:
rename{old = "temp.lua", new ="temp1.lua"}