变长参数

Lua中的函数可以接受不同数量的实参。例如:

1
2
3
4
5
6
7
8
function add(...)
local sum = 0
for i,v in ipairs({...}) do
sum = sum + v
end
return sum
end
print(add(1,2,3,4,5)) -- 15

参数表中的三个...表示变长参数,函数访问变成参数也需要使用...。变长参数只能放在最后一位。

可以通过select函数来访问变长参数。

1
2
3
4
5
6
7
8
9
10
11
function test(...)
print(select('#', ...))
for i=1, select('#', ...) do
print(select(i, ...))
end
end

test('a', nil, 2, 1)

-- select('#', ...) 返回变长参数的长度
-- select(i, ...) 返回第i个位置的实参

具名参数

Lua中调用函数的实参是通过形参的位置相互对应起来的,第一个传入的参数赋值给第一个形参,以此类推。通过名字来指定实参也是很有必要的。

例如:os.rename()方法用于修改文件名称,但是很容易忘记第一个参数是原来的名字还是新的名字。所以需要以下的写法:os.rename(old='a1',new='a2'),但是lua并不支持这样的写法。需要将写法改成传入一个table:os.renname{old='a1', new='a2'}。可以对os.rename方法进行一次简单的封装。

1
2
3
function rename(args)
os.rename(args.old, args.new)
end

深入函数

Lua中函数是“第一类值”,它们具有特定的词法域。

“第一类值”表示函数与其他基本类型具有相同的权利,可以赋值给变量、table可以作为函数的返回值,也可以作为函数实参。

词法域,是函数中可以嵌套函数,内部函数可以访问外部函数的变量。

在Lua中函数与其他值一样都是匿名的,即他们都没有名称。

Lua中常见的函数写法如下:function a() print('a') end,同时也可以写成这样:a = function () print('a') end

例如:

1
2
3
4
5
6
7
8
names = {"a", "b", "c"}
ages = {a=6, b=2, c=5}

table.sort(names, function(c1,c2) return ages[c1] > ages[c2] end)

for i,v in ipairs(names) do
print(v)
end

上面的例子描述了对names进行排序,可以抽象出一个函数来实现:

1
2
3
function sortbyage(names, ages)
table.sort(names, function(c1,c2) return ages[c1] > ages[c2] end)
end

传递给sort的匿名函数可以访问参数ages,而ages是外部函数sortbyage的局部函数。在这个匿名函数内部,ages既不是全局变量也不是局部变量,称其为“非局部的变量”。

下面是一个关于计数器的例子:

1
2
3
4
5
6
7
8
9
10
11
function count()
local i = 0
return function() i = i + 1 return i end
end

a = count()
b = count()
print(a()) --> 1
print(a()) --> 2
print(b()) --> 1
print(b()) --> 2

上面这段代码,匿名函数访问了一个“非局部的变量”i,该变量用于保存一个计数器。初看,由于创建变量i的函数已经返回,所以之后每次调用匿名函数时,i都应是超出了作用范围的。但其实不然,lua会以闭包的感念来正确的处理这种情况。简单的来讲,一个闭包就是一个函数加上该函数所需要访问的所有“非局部变量”。

非全局的函数

由于函数是一种“第一类值”,因此函数也可以存储在table的字段当中和局部变量中。

1
2
3
4
5
6
Lib = {}
Lib.foo = function (x,y) return x + y end

local f = function (<参数>)
<函数体>
end

—EOF—