Lua 基础之函数进阶

本文深入探讨了Lua中的闭包概念,解释了如何在函数内部访问外部局部变量。同时,讨论了非全局函数的定义,特别是递归局部函数的正确写法,强调了使用`local`关键字的重要性。文章还总结了避免先调用后定义错误的最佳实践,并阐述了尾调用的概念,指出其在内存管理上的优势。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

闭包 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())
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值