《Lua程序设计(第2版)》 6.3 正确的尾调用(proper tail call)
Lua是支持尾调用消除(tail-call elimination)的,如下面对函数g的调用就是尾调用。
function f(x) return g(x) end
尾调用之后,程序不需要保存任何关于函数f的栈(stack)信息,即不耗费任何栈空间。
尾调用方法可用于编写状态机(state machine),类似于goto到另一个函数,如果没有尾调用消除,每次调用都会创建一个新的栈层(stack level),若干步之后有可能导致栈溢出。
注意,以下几种情况均不是尾调用:
function f(x)
g(x)
end return g(x) +
return x or g(x)
return (g(x))
举例,书中有个迷宫的例子,代码如下:
入口 room1 |
room2 |
room3 |
出口 room4 |
function room1 ()
local direction = io.read()
if direction == "east" then
room2()
elseif direction == "south" then
room3()
else
print("invalid direction!")
room1()
end
end function room2 ()
local direction = io.read()
if direction == "west" then
return room1()
elseif direction == "south" then
return room4()
else
print("invalid direction!")
return room2()
end
end function room3 ()
local direction = io.read()
if direction == "north" then
return room1()
elseif direction == "east" then
return room4()
else
print("invalid direction!")
return room3()
end end function room4 ()
print("congratulations!")
end room1()
运行结果:
south
west
invalid direction!
east
congratulations!
我们写一个尾调用的简单循环:
function room1()
return room2()
end function room2()
print()
return room1()
end room1()
运行结果:
-- 一直循环不会报错
不使用尾调用:
function room1()
room2()
end function room2()
print()
room1()
end room1()
运行结果:
-- 循环几秒之后出现以下报错: lua: stack overflow
stack traceback:
[C]: in function 'print'
6_propertailcall.lua:: in function 'room2'
6_propertailcall.lua:: in function 'room1'
6_propertailcall.lua:: in function 'room2'
6_propertailcall.lua:: in function 'room1'
6_propertailcall.lua:: in function 'room2'
6_propertailcall.lua:: in function 'room1'
6_propertailcall.lua:: in function 'room2'
6_propertailcall.lua:: in function 'room1'
6_propertailcall.lua:: in function 'room2'
...
6_propertailcall.lua:: in function 'room2'
6_propertailcall.lua:: in function 'room1'
6_propertailcall.lua:: in function 'room2'
6_propertailcall.lua:: in function 'room1'
6_propertailcall.lua:: in function 'room2'
6_propertailcall.lua:: in function 'room1'
6_propertailcall.lua:: in function 'room2'
6_propertailcall.lua:: in function 'room1'
6_propertailcall.lua:: in main chunk
[C]: ?