Lua
语言中一种与众不同但又非常有用的特性是允许一个函数返回多个结果。 Lua
语言中几个预定义函数就会返回多个值。例如函数 string.find
,该函数用于在字符串中定位模式。当找到了对应的模式时,该函数会返回两个索引值:所匹配模式在字符串中起始字符和结尾字符的索引。使用多重赋值可以同时获取到这两个结果:
s, e = string.find("hello `Lua` users", "`Lua`") print(s, e) --> 7 9
Lua
语言编写的函数同样可以返回多个结果,只需要在 return
关键字后列出所有要返回的值即可。例如:一个用于查找序列中最大元素的函数可以同时返回最大值及该元素的位置:
function maximum (a) local mi =1 -- 最大值的索引 local m = a[mi] -- 最大值 for i = 1 #a do if a[i] > m then mi = i m = a[i] end end return m, mi -- 返回最大值及其索引 end print(maximum({8, 10, 23, 12, 5})) --> 23 3
Lua
语言根据函数的被调用情况调整返回值的数量。
- 当函数被作为一条单独语句调用时,其所有返回值都会被丢弃
- 当函数被作为表达式(例如,加法的操作数)调用时,将只保留函数的第一个返回值
- 只有当函数调用是一系列表达式中的最后一个表达式(或是唯一一个表达式)时,其所有的返回值才能被获取到。
这里所谓的“一系列表达式”在 Lua
中表现为4种情况:
- 多重赋值
- 函数调用时传入的实参列表
- 表构造器
return
语句。
如下所示:
function foo0 () end -- 不返回结果 function foo1 () return "a" end -- 返回1个结果 function foo2 () return "a", "b" end -- 返回两个结果
在多重赋值中,如果一个函数调用是一系列表达式中的最后(或者是唯一)一个表达式,则该函数调用将产生尽可能多的返回值以匹配待赋值变量:
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 = foo2() -- x="a", y="b", z=nil
请注意,只有当函数调用是一系列表达式中的最后(或者是唯一)一个表达式时才能返回多值结果,否则只能返回一个结果:
x, y = foo2(), 20 -- x="a", y=20 ('b'被丢弃) x, y = foo0(), 20, 30 -- x=nil, y=20 (30被丢弃)
当一个函数调用是另一个函数调用的最后一个(或者是唯一)实参时,第一个函数的所有返回值都会被作为实参传给第二个函数。例如函数 print
,由于函数 print
能够接受可变数量的参数,所以 print(g())
会打印出g返回的所有结果。
print(foo0()) --> (没有结果) print(foo1()) --> a print(foo2()) --> a b print(foo2(), 1) --> a 1 print(foo2() .. "x") --> ax
当在表达式中调用 foo2
时, Lua
语言会把其返回值的个数调整为 1
。因此,在上例的最后一行,只有第一个返回值" a
"参与了字符串连接操作。
当调用 f(g())
时,如果f的参数时固定的,那么 Lua
语言会把g返回值的个数调整成与f的参数个数一致。这并非巧合,实际上这正是多重赋值的逻辑。
表构造器会完整的接受函数调用的所有返回值,而不会调整返回值的个数:
t = {foo0()} -- t = {} (一个空表) t = {foo1()} -- t = {"a"} t = {foo2()} -- t = {"a", "b"}
不过,这种行为只有当函数调用是表达式列表中的最后一个时才有效,在其他位置上的函数调用总是只返回一个结果:
t = {foo0(), foo2(), 4} -- t[1] = nil, t[2] = "a", t[3] = 4
最后,形如 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((foo0())) --> nil print((foo1())) --> a print((foo2())) --> a
return
语句后面的内容是不需要加括号的,如果加了括号会导致程序出现额外的行为。因此,无论 f
究竟返回几个值,形如 return (f(x))
的语句只返回一个值。有时这可能是我们所希望出现的情况,但有时又可能不是。