Lua程序设计(3)

Lua中的函数。

函数

在Lua语言中,函数是对语句和表达式进行抽象的主要方式。函数调用时都需要使用一对圆括号把参数列表扩起来,无论函数需不需要参数。当函数只有一个参数,且该参数是字符串常量或表构造器的时候,括号是可选的。

一例:

1
2
3
4
5
6
7
function add (a)
local sum = 0
for i = 1,#a do
sum = sum + a[i]
end
return sum
end

调用函数时使用的参数个数可以与定义函数时使用的参数个数不一致。Lua语言会通过抛弃多余参数和将不足的参数设为nil的方式来调整参数的个数。

多返回值

Lua编写的函数可以返回多个结果,只需要在return关键字后列出所有要返回的值即可。

  • 当函数被作为一条单独语句调用时,其所有返回值都会被丢弃
  • 当函数被作为表达式调用时,将只保留函数的第一个返回值
  • 当函数调用是一系列表达式中的最后一个表达式(或者是唯一一个表达式)时,其所有的返回值才能被获取到
  • 如果一个函数没有返回值或者返回值不够多,那么Lua语言会用nil来补充缺失的值

可变长参数函数

Lua语言中的函数可以是可变长参数函数,即可以支持数量可变的参数。

1
2
3
4
5
6
7
function add (...)
local s = 0
for _, v in ipairs{...} do
s = s + v
end
return s
end

参数列表中的三个点(…)表示该函数的参数是可变长的。当这个函数被调用时,Lua内部会把它的所有参数收集起来,我们把这些被收集起来的参数称为函数的额外参数。当函数要访问这些参数时仍需用到三个点,但不同的是此时这三个点时作为一个表达式来使用的。在上例中,表达式{…}的结果是一个由所有可变长参数组成的列表,该函数会遍历该列表来累加其中的元素。

我们将三个点组成的表达式称为可变长参数表达式,其行为类似于一个具有多个返回值的函数,返回的是当前函数的所有可变长参数。

遍历可变长参数

要遍历可变长参数,函数可以使用表达式{...}将可变长参数放在一个表中,就像add示例中所做的那样。不过在某些情况下,如果可变长参数重包含无效的nil,那么{...}获得的表可能不再是一个有效的序列,此时可以使用table.pack将表达式中的所有参数放在一个表中返回,这个表还有一个保存了参数个数的额外字段”n”。

另一种遍历函数的可变长参数的方法是使用函数select,函数select总是具有一个固定的参数selector,以及数量可变的参数,如果selector是数值n,那么函数select返回第n个参数后的所有参数(包括第n个);否则,selector应该是字符串#,以便函数select返回额外参数的总数

函数table.unpack

多重返回值还涉及一个特殊的函数table.unpack,该函数的参数是一个数组,返回值为数组内的所有元素。

unpack函数的重要用途之一体现在泛型调用机制中,泛型调用机制允许我们动态的调用具有任意参数的任意函数。如果我们想通过数组a传入可变的参数来调用函数f:

1
f(table.unpack(a))

unpack会返回a中所有的元素,而这些元素又被用作f的参数:

1
print(string.find("hello","ll"))

可以使用以下的代码动态地构造一个等价的调用:

1
2
3
4
f = string.find
a = {"hello","ll"}

print(f(table.unpack(a)))

unpack可以显式的限制返回元素的范围:

1
2
print(table.unpack({"Sun","Mon","Tue","Wed"},2,3))
> Mon Tue

正确的尾调用

为调用是被当作函数调用使用的跳转。当一个函数的最后一个动作是调用另一个函数而没有再进行其他工作时,就形成了尾调用

当被调用的函数执行结束后,程序就不再需要返回最初的调用者,因此,在尾调用之后,程序也就不需要在调用栈中保存有关调用函数的任何信息,当g返回时,程序的执行路径会直接返回到调用f的位置。由于尾调用不会使用栈空间,所以一个程序中能够嵌套的尾调用的数量是无限的。

在Lua语言中,只有形如return func(args)的调用才是尾调用。