《Lua程序設計(第2版)》 6.3 正確的尾調用(proper tail call)
Lua是支持尾調用消除(tail-call elimination)的,如下面對函數g的調用就是尾調用。
1 function f(x) return g(x) end
尾調用之后,程序不需要保存任何關於函數f的棧(stack)信息,即不耗費任何棧空間。
尾調用方法可用於編寫狀態機(state machine),類似於goto到另一個函數,如果沒有尾調用消除,每次調用都會創建一個新的棧層(stack level),若干步之后有可能導致棧溢出。
注意,以下幾種情況均不是尾調用:
1 function f(x) 2 g(x) 3 end 4 5 return g(x) + 1 6 return x or g(x) 7 return (g(x))
舉例,書中有個迷宮的例子,代碼如下:
入口 room1 |
room2 |
room3 | 出口 room4 |
1 function room1 () 2 local direction = io.read() 3 if direction == "east" then 4 room2() 5 elseif direction == "south" then 6 room3() 7 else 8 print("invalid direction!") 9 room1() 10 end 11 end 12 13 function room2 () 14 local direction = io.read() 15 if direction == "west" then 16 return room1() 17 elseif direction == "south" then 18 return room4() 19 else 20 print("invalid direction!") 21 return room2() 22 end 23 end 24 25 function room3 () 26 local direction = io.read() 27 if direction == "north" then 28 return room1() 29 elseif direction == "east" then 30 return room4() 31 else 32 print("invalid direction!") 33 return room3() 34 end 35 36 end 37 38 function room4 () 39 print("congratulations!") 40 end 41 42 room1()
運行結果:
south
west
invalid direction!
east
congratulations!
我們寫一個尾調用的簡單循環:
1 function room1() 2 return room2() 3 end 4 5 function room2() 6 print(111) 7 return room1() 8 end 9 10 room1()
運行結果:
111 111 -- 一直循環不會報錯
不使用尾調用:
1 function room1() 2 room2() 3 end 4 5 function room2() 6 print(111) 7 room1() 8 end 9 10 room1()
運行結果:
111 111 -- 循環幾秒之后出現以下報錯: lua: stack overflow stack traceback: [C]: in function 'print' 6_propertailcall.lua:47: in function 'room2' 6_propertailcall.lua:43: in function 'room1' 6_propertailcall.lua:48: in function 'room2' 6_propertailcall.lua:43: in function 'room1' 6_propertailcall.lua:48: in function 'room2' 6_propertailcall.lua:43: in function 'room1' 6_propertailcall.lua:48: in function 'room2' 6_propertailcall.lua:43: in function 'room1' 6_propertailcall.lua:48: in function 'room2' ... 6_propertailcall.lua:48: in function 'room2' 6_propertailcall.lua:43: in function 'room1' 6_propertailcall.lua:48: in function 'room2' 6_propertailcall.lua:43: in function 'room1' 6_propertailcall.lua:48: in function 'room2' 6_propertailcall.lua:43: in function 'room1' 6_propertailcall.lua:48: in function 'room2' 6_propertailcall.lua:43: in function 'room1' 6_propertailcall.lua:51: in main chunk [C]: ?