先说个容易犯的错误。

下面这种写法是错误的。

1
2
3
4
5
local fact = function (n)
if n == 0 then return 1
else return n*fact(n-1)
end
end

错误就在于,Lua编译到fact(n-1)时,局部的fact尚未定义完,此时调用的是全局的fact。改正的写法如下:

1
2
3
4
5
6
local fact
fact = function (n)
if n == 0 then return 1
else return n*fact(n-1)
end
end

这还有个术语,叫前向声明(forward declaration)。

下面我们来看看尾调用。都知道尾调用不会耗费栈空间,所以一个程序可以拥有无数嵌套的尾调用。例如,调用下面的函数时,传入任何数字作为参数都不会造成栈溢出:

1
2
3
function foo (n)
if n > 0 then return foo(n - 1) end
end

不过要仔细甄别是否是尾调用。标准就是看函数在调用之后是不是什么也不做。下面举几个反例,都不是尾调用:

1
function f(x) g(x) end -- 必须丢弃g返回的临时结果
1
2
3
return g(x) + 1 -- 必须做一次加法
return x or g(x) -- 必须调整为一个返回值
return (g(x)) -- 必须调整为一个返回值

一般来说,必须要符合“return ()”的形式。

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

尾调用的一大应用就是编写状态机。比如常见的迷宫游戏的例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
function room1 ()
local move = io.read()
if move == "south" then return room3()
elseif move == "east" then return room2()
else
print("invalid move")
return room1()
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