闭包 closure
- 函数可以嵌套函数,内层函数可以访问外层函数的局部变量,这种特性叫做和函数闭包
- lua 中除了全局变量和局部变量之外,还有一种叫“非局部变量”的变量
- 闭包 closure 的概念是一个函数和该函数能访问的所有“非局部变量”,lua 其实只有闭包,没有函数,函数只是特殊的闭包而已,即没有“非局部变量”
function counter()
local i = 0
return function()
i = i + 1
return i
end
end
-- c1 c2 是两个不相干的闭包,这个闭包包括一个匿名函数和一个非局部变量 i
c1 = counter()
c2 = counter()
print(c1()) -- 1
print(c2()) -- 1
print(c2()) -- 2
print(c2()) -- 3
print(c1()) -- 2
student = {{name = "lin", grade = 90}, {name = "qiang", grade = 85}, {name = "li", grade = 79}, {name = "jia", grade = 80}}
-- 按名字排序
sortByName = function(student)
table.sort(
student,
function(s1, s2)
return s1.name < s2.name
end
)
end
-- 按成绩排序
sortByGrade = function(student)
table.sort(
student,
function(s1, s2)
return s1.grade < s2.grade
end
)
end
-- 输出学生信息
printStu = function(student)
for i, stu in ipairs(student) do
local str = ""
for k, v in pairs(stu) do
str = str .. k .. " := " .. v .. " "
end
print(str)
end
end
sortByName(student)
printStu(student)
sortByGrade(student)
printStu(student)
非全局函数
- 定义局部函数和定义全局函数一样,只是要在前面加上 local 而已,因此有下面两种定义方式
local add = function(...)
local arg = {...}
local sum = 0
for i, v in ipairs(arg) do
sum = sum + v
end
return sum
end
local function max(a, b)
return a > b and a or b
end
- 定义递归的局部函数时要注意,使用
local var = function() ... end
这种方式定义函数可能会出错,请看下面计算阶乘的三个函数定义
-- 正确
local function fact(n)
if n <= 1 then
return 1
end
return n * fact(n - 1)
end
-- 错误
local fact = function(n)
if n <= 1 then
return 1
end
return n * fact(n - 1)
end
-- 正确
local fact
fact = function(n)
if n <= 1 then
return 1
end
return n * fact(n - 1)
end
- 先看第二个,也就是错误的那个定义。这种方式定义的 fact 是一个局部变量,它的值是一个函数,而这个函数还没有定义完,因此 fact 的定义也还没完成;当调用 fact 函数执行到
return n*fact(n-1)
时,由于局部函数 fact 还没有定义完成,因此程序自动把 fact 当成全局变量,而全局的 fact 并未定义,因此就出错了。 - 正确的定义是第三个,即先定义局部变量 fact ,再给这个变量赋值。这种方式下调用 fact 时即使 fact 的值还没求出来(函数定义还没完成),但 fact 变量的定义是完成的了,所以在递归的时候就不会出现找不到局部变量 fact 的情况。
- 第一个定义是“语法糖”,其展开后跟第三个一样,因此也不会出错。
- 如果是间接递归,第一个定义也会有问题
local function f(n)
if n <= 0 then
return
end
print("f:", n)
g(n - 1)
end
-- 错误
local function g(n)
if n <= 0 then
return
end
print("g:", n)
f(n - 1)
end
- 这个错误出现的主要原来是 f 函数找不到 g 函数的定义,虽然 g 的定义展开后也是合法的,但在 f 定义的地方 g 还没有
总结
函数必须先定义再调用,非全局函数为了避免出现先调用再定义的错误,最好使用下面这种定义方式
local f , g
f = function() ... end
g = function() ... end
尾调用
- 尾调用是指一个函数调用另一个函数的语句是该函数的最后一条语句,且调用完该函数就返 回了,不再做其它事情
- 尾调用类似于 goto 语句,直接从当前函数跳转到另一个函数,执行完之后不需要再返回当 前函数,因此无需保存当前函数的堆栈信息,因此尾调用可以无限调用而不用担心内存溢出
- 尾调用的关键就是函数调用发生在最后一条语句且调用完之后直接返回,以下情况不属 于尾调用
function f() g() end -- 调用 g 之后并没有立即返回
return g() + 1 -- 调用 g 之后还要任运算
return x or g()
return (g())