Lua中函数的另一个有趣的特征是可以正确的处理尾调用(proper tail recursion)。
尾调用是一种类似在函数结尾的goto
调用,当函数最后一个动作是调用另外一个函数时,我们称这种调用尾调用。
function f()
return g()
end
g
的调用是尾调用。
例子中f
调用g
后不会再做任何事情,这种情况下当被调用函数g
结束时程序不需要返回到调用者f
;
所以尾调用之后程序不需要在栈中保留关于调用者的任何信息。一些编译器比如Lua解释器利用这种特性在处理尾调用时不使用额外的栈,
我们称这种语言支持正确的尾调用。
由于尾调用不需要使用栈空间,那么尾调用递归的层次可以无限制的。
例如下面调用不论n
为何值不会导致栈溢出。
function foo(n)
if n > 0 then return foo(n - 1) end
end
需要注意的是:必须明确什么是尾调用。
一些调用者函数调用其他函数后也没有做其他的事情但不属于尾调用。比如:
function f(x)
g(x)
return
end
上面这个例子中f
在调用g
后,不得不丢弃g
地返回值,所以不是尾调用,同样的下面几个例子也不是尾调用:
return g(x) + 1 -- must do the addition
return x or g(x) -- must adjust to 1 result
return (g(x)) -- must adjust to 1 result
Lua中类似return g(...)
这种格式的调用是尾调用。
但是g
和g
的参数都可以是复杂表达式,因为Lua会在调用之前计算表达式的值。
例如下面的调用是尾调用:
return x[i].foo(x[j] + a*b, i + j)
如果没有正确的尾调用,每次调用都要创建一个栈,多次调用后可能导致栈溢出。
但正确的尾调用可以无限制的尾调用,因为每次尾调用只是一个goto
到另外一个函数并不是传统的函数调用。