尾调用

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(...)这种格式的调用是尾调用。 但是gg的参数都可以是复杂表达式,因为Lua会在调用之前计算表达式的值。 例如下面的调用是尾调用:

return x[i].foo(x[j] + a*b, i + j)

如果没有正确的尾调用,每次调用都要创建一个栈,多次调用后可能导致栈溢出。 但正确的尾调用可以无限制的尾调用,因为每次尾调用只是一个goto到另外一个函数并不是传统的函数调用。

results matching ""

    No results matching ""