Lua中函数的另一个有趣的特征是可以正确的处理尾调用(proper tail recursion,一些书使用术语“尾递归”,虽然并未涉及到递归的概念)。
尾调用是一种类似在函数结尾的goto调用,当函数最后一个动作是调用另外一个函数时,我们称这种调用尾调用。例如:
function f(x)
return g(x)
end
g的调用是尾调用。
例子中f调用g后不会再做任何事情,这种情况下当被调用函数g结束时程序不需要返回到调用者f;所以尾调用之后程序不需要在栈中保留关于调用者的任何信息。一些编译器比如Lua解释器利用这种特性在处理尾调用时不使用额外的栈,我们称这种语言支持正确的尾调用。
由于尾调用不需要使用栈空间,那么尾调用递归的层次可以无限制的。例如下面调用不论n为何值不会导致栈溢出。
------解决方案--------------------------------------------------------
一般函数调用,栈都会保存函数的返回地址、返回值地址、参数列表。
按照你举例子,g(x)的参数,可以使用f(x)中的变量的地址,g(x)的返回值,可以使用f(x)的返回值地址,g(x)后面没有其他调用,所以,g(x)的函数返回地址空间,也可以直接使用f(x)的地址空间。
这样看,在调用g(x)的时候,系统无需为其分配任何新的空间。
------解决方案--------------------------------------------------------
这样的尾调用实现递归有个缺陷,就是需要依赖于外部变量实现递归返回控制,语句话尾调用函数不能带用来控制递归次数的参数,否则将变成常规栈递归,好在Lua可以在函数中创建子函数闭包加以利用,不会污染到常规变量名称空间。