在Lua中,函数是一种“第一类值”,它们具有特定的词法域。“词法域”是什么意思呢?这是指一个函数可以嵌套在另一个函数中,内部的函数可以访问外部函数中的变量。
在Lua中有一个容易混淆的概念是,函数与所有其他值一样是匿名的,即它们都没有名称。当讨论一个函数名时,实际上是在讨论一个持有某函数的变量。函数定义实际上是一个表达式:
function foo(x) return 2*x end其实际是foo = function(x) return 2*x end, 其实其是一条赋值语句。
可以将表达式“function(x) < body > end”视为一个函数的构造式,将构造式的结果称为一个匿名函数。
一个函数可以作为另一个函数的参数,接受另一个函数作为实参的函数,称为高阶函数。
network = {{name = "grauna", IP="210.26.30.34"}
{name = "lua", IP = "210.26.23.12"}}
table.sort(network, function(a, b) return (a.name > b.name) end) -->传入一个匿名函数为参数
若将一个函数写在另一个函数内,那么这个位于内部的函数可以访问外部函数中的局部变量,这项特征被称为“词法域”。为什么在Lua中允许这种访问呢?原因在于函数是“第一类值”。看如下代码:
function newCounter()
local i = 0
return function()
i = i + 1
return i
end
end
local c1= newCounter()
c1() -->1
c1() -->2
简单来讲,一个closure就是一个函数加上这个函数访问的所有“非局部变量”。如果再次调用newCounter,那么它会创建一个新的closure。
local c2 = newCounter()
print(c2()) -->1
print(c1()) -->3
从技术上讲,Lua中只有closure而没有函数,函数只是一种特殊的closure。
closure还有一些常用的用法,比如重定义一个预设的函数:
oldSin = math.sin
math.sin = function(x)
return oldSin(x*math.pi . 180)
end
可以使用同样的技术创建一个安全的运行环境,即所谓的“沙盒”。
do
local oldOpen = io.open
local access_OK = function(filename, mode)
< 检查一些访问权限 >
end
io.open = function(filename, mode)
if access_Ok(filename, mode) then
return oldOpen(filename, mode)
else
return nil, "access denied"
end
end
end
在Lua中,函数通常存储在一个table中,而不是作为全局变量使用:
Lib1 = {}
Lib1.foo = function(x, y) return x + y end
Lib2 = {foo = function(x,y) return x + y end}
除此之外,Lua还提供了另一种语法来定义这类函数:
Lib = {}
function Lib.foo(x,y) return x+y end
对于这种局部函数的定义,Lua还支持一种特殊的“语法糖”
local function foo(< 参数 >)
<函数体>
end
另外在定义递归的局部函数有一点要特别注意。看以下代码:
local fact = function(n)
if n == 0 then return 1
else return n*fact(n-1)
end
end
这样定义的错误的,关键在于其先计算右边的表达式,然后再定义左边变量,而在调用fact(n-1)时,fact变量尚未定义完毕,因此其实际会调用全局的fact,而非其本身。
正确的写法如下,应先定义局部变量fact:
local fact
fact = function(n)
if n==0 then return 1
else return n * fact(n -1)
end
end
而对于local function foo<参数> <函数体> end的定义方式,Lua将其展开为
local foo
foo = function(<参数>) <函数体> end
所以使用这种方式定义递归函数不会有错误。
当然,这个技巧对于间接递归的函数是无效的,你必须使用一个明确的前置声明。
local f,g
function g()
f()
end
function f() -->注意不要用local function,否则其会创建一个f覆盖前面的变量
g()
end
Lua支持“尾调用消除”。所谓“尾调用”就是一种类似于goto的函数调用。当一个函数调用时另一个函数的最后一个动作时,该调用才算是一条“尾调用”。
function f(x) return g(x) end
在上述调用中,当f调用完g后便无事可做了,因此程序不在需要保留函数f的堆栈信息,所以在进行尾调用时不耗费任何栈空间,这种实现便是“尾调用消除”。由于“尾调用”不会消耗栈空间,所以一个程序可以拥有无数嵌套的“尾调用”:
function foo(n)
if n > 0 then return foo(n-1) end
end
在Lua中“尾调用”的一大应用就是编写“状态机”。这种程序以一个函数来表示一个状态。