05-函数
争取两三周入门Lua!
函数(Function)是对语句和表达式进行抽象的主要方式。
函数的定义与调用
函数的定义如下:
function 函数名(参数列表)
函数体
return xxx
end
调用函数时使用的参数个数可以与定义函数时使用的参数个数不一致,Lua会抛弃多余参数并将不足的参数设为nil
:
function f(a, b)
print(a, b)
end
f() --> nil nil
f(1) --> 1 nil
f(1, 2) --> 1 2
f(1, 2, 3) --> 1 2 (3被丢弃)
这样的特性可以实现函数的默认实参:
function incCount(n)
n = n or 1 -- 如果n为nil, 那么n为1
globalCounter = globalCounter + n
end
多返回值
Lua允许一个函数返回多个结果,例如string.find()
:
s, e = string.find("hello Lua", "Lua") --> 7 9
要让自定义函数返回多个值,只需在return
后列出所有要返回的值即可:
--- 找到最大元素及其位置
function getMax(a)
local idx = 1
local val = a[idx]
for i = 1, #a do
if a[i] > val then
idx = i
val = a[i]
end
end
return idx, val
end
条件
Lua会根据函数的被调用情况调整返回值的数量:
- 函数作为一条单独语句调用时,所有返回值都会被丢弃;
- 函数被表达式调用时(例如作为加法的操作数),将只保留第一个返回值;
- 函数作为最后一个表达式使用时(多重赋值、函数调用时传入的实参列表、表构造器和
return
语句),获取所有返回值。
这里看看最后一条:
多重赋值:
function foo() return 1, 2 end x, y = foo() -- 1 2 x, y, z = foo() -- 1 2 nil x, y = foo(), 5 -- 1 5, 这里foo()不是最后一个表达式, 因此只保留第一个返回值
函数调用时传入的实参列表:
print(foo()) -- 1 2 print(foo(), 5) -- 1 5
表构造器:
a = {foo()} -- a[1] = 1, a[2] = 2 a = {foo(), 5} -- a[1] = 1, a[2] = 5
return
语句:function foo2() return foo() end print(foo2()) -- 1 2 print((foo2())) -- 1
table.unpack()
该函数可以将一个表转换成一组返回值:
table.unpack({1,2,3}) -- 1 2 3
a, b = table.unpack({1, 2, 3}) -- 1 2 (3被丢弃)
该函数的重要用途之一体现在泛型调用机制中,该机制允许我们调用具有任意参数的任意函数。例如下面两个写法是等价的,但第二种泛型调用写法更通用:
string.find("hello", "ll")
f = string.find
a = {"hello", "ll"}
f(table.unpack(a))
此外,该函数还有两个参数用来显式限制返回元素的范围:
table.unpack({1, 2, 3}, 2, 3) -- 2 3
可变长函数参数
遍历参数
使用...
表示该函数的参数是可变长的,例如一个求和函数:
function add(...)
local sum = 0
for _, v in ipairs{...} do
sum = sum + v
end
return sum
end
print(add(1, 2, 3)) -- 6
要想使用参数列表,需要用{...}
获取由所有可变长参数组成的列表,然后才能遍历。如果参数列表中包含nil
,就不能这样做。需要使用table.pack(...)
,该函数获取所有可变长参数,将其打包入表中,然后添加一个额外字段”n”,内容是表的长度。
另一种遍历参数的方法是使用函数select
,它有一个固定参数selector
以及可变参数,有两种用法:
-- selector为数值n, 返回第n个参数和它后面的参数
print(select(2, "a", "b", "c")) -- b c
-- selector为"#", 返回额外参数的总数
print(select("#", "a", "b", "c")) -- 3
可以这样遍历上面的add(...)
:
function add(...)
local sum = 0
for i = 1, select("#", ...) do
sum = sum + select(i, ...)
end
return sum
end
不过这样做性能消耗有点大,不如for循环。
应用
也能使用变长参数来模拟普通的参数传递机制:
function foo(a, b, c)
-- 可写成
function foo(...)
local a, b, c = ...
如果想要格式化输出文本,需要使用string.format
和io.write
函数,我们可以用可变长函数参数将它俩结合起来:
function fwrite(fmt, ...)
return io.write(string.format(fmt, ...))
end