第一版是为 Lua 5.0 编写的。虽然在很大程度上仍然适用于更高版本,但还是存在一些差异。
第四版针对 Lua 5.3,可在 亚马逊 和其他书店购买。
购买本书,您还将帮助支持 Lua 项目。
用 Lua 编程 | ||
第一部分。语言 第 6 章。关于函数的更多内容 |
Lua 中函数的另一个有趣特性是它们执行正确尾调用。(尽管该概念并不直接涉及递归,但一些作者使用了术语正确尾递归。)
尾调用是一种伪装成调用的 goto。当函数将其最后操作作为另一个函数的调用时,就会发生尾调用,因此它没有其他操作可做。例如,在以下代码中,对 g
的调用是尾调用
function f (x) return g(x) end在
f
调用 g
之后,它没有其他操作可做。在这种情况下,当被调用函数结束时,程序不需要返回到调用函数。因此,在尾调用之后,程序不需要在堆栈中保留任何有关调用函数的信息。一些语言实现(例如 Lua 解释器)利用了这一事实,并且在执行尾调用时实际上不会使用任何额外的堆栈空间。我们说这些实现支持正确尾调用。
由于正确尾调用不使用堆栈空间,因此程序可以进行的“嵌套”尾调用的数量没有限制。例如,我们可以使用任何数字作为参数调用以下函数;它永远不会溢出堆栈
function foo (n) if n > 0 then return foo(n - 1) end end
在使用正确尾调用时,一个微妙的要点是什么是尾调用。一些明显的候选不符合调用函数在调用后无事可做的标准。例如,在以下代码中,对 g
的调用不是尾调用
function f (x) g(x) return end该示例中的问题在于,在调用
g
之后,f
在返回之前仍必须丢弃 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
及其参数都可以是复杂表达式,因为 Lua 在调用之前对其进行求值。例如,下一个调用是尾调用
return x[i].foo(x[j] + a*b, i + j)
正如我之前所说,尾调用是一种 goto。因此,Lua 中正确尾调用的一个非常有用的应用是编程状态机。此类应用可以用一个函数表示每个状态;更改状态就是转到(或调用)一个特定函数。例如,让我们考虑一个简单的迷宫游戏。迷宫有几个房间,每个房间最多有四个门:北、南、东、西。在每一步中,用户输入一个移动方向。如果在该方向上有一扇门,用户将转到相应的房间;否则,程序将打印一个警告。目标是从初始房间移动到最终房间。
这个游戏是一个典型的状态机,其中当前房间是状态。我们可以为每个房间实现一个函数来实现此类迷宫。我们使用尾调用从一个房间移动到另一个房间。一个有四个房间的小迷宫可能如下所示
function room1 () local move = io.read() if move == "south" then return room3() elseif move == "east" then return room2() else print("invalid move") return room1() -- stay in the same room end end function room2 () local move = io.read() if move == "south" then return room4() elseif move == "west" then return room1() else print("invalid move") return room2() end end function room3 () local move = io.read() if move == "north" then return room1() elseif move == "east" then return room4() else print("invalid move") return room3() end end function room4 () print("congratulations!") end我们通过调用初始房间来开始游戏
room1()如果没有正确的尾调用,每个用户移动都会创建一个新的堆栈级别。在一些移动之后,将发生堆栈溢出。使用正确的尾调用,用户可以进行的移动次数没有限制,因为每个移动实际上执行的是到另一个函数的 goto,而不是常规调用。
对于这个简单的游戏,您可能会发现数据驱动的程序(您使用表描述房间和移动)是一个更好的设计。然而,如果游戏中每个房间都有几个特殊情况,那么这种状态机设计非常合适。
版权所有 © 2003–2004 Roberto Ierusalimschy。保留所有权利。 |