Lua闭包,词法定界,第一类型值笔记

本文深入解析Lua中的闭包概念,包括函数作为第一类型值、词法定界、闭包的应用场景如创建迭代器、作为高阶函数参数及重新定义函数。通过实例展示了闭包如何保留非局部变量状态。

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

Lua支持闭包的语法特性:

1.函数是第一类型值:第一类型值表示函数与其他传统类型的值(例如数字和字符串类型)具有相同的权利。即函数可以存储在变量中,可以作为实参传递给其他函数,还可以作为其他函数的返回值。以下两种定义函数的方式是等价的:

    local function f1()
        print("Hello")
    end
    --等价于
    local f1 = function()
        print("Hello")
    end

2.词法定界:Lua中函数可以访问函数之外定义的值,该值可以定义在其他函数内,也可以直接定义在文件之内。该值叫做此函数的非局部变量或upvalue。通过下面例子可以看出函数可以对非局部变量的正确访问。

--f1访问未被定义在函数中的num
 local num = 0
 local function f1()
     print(num)
 end
 f1()--输出0

 --f3访问被定义在f2函数中的count
 local function f2()
     local count = 5
     local function f3()
         print(count)
     end
     f3()
 end
 f2()--输出5

3.闭包概念:Lua函数在多次调用时,可以正确访问他上次使用的非局部变量的值,这样的语法特性叫闭包。

广义来说,只要用到了外部定义的变量的函数,就使用了闭包特性。

通过下面例子可以看出函数多次调用时,上次使用的非局部变量的值得以保留。

--f1访问未被定义在函数中的num
local num = 0
local function f1()
    num = num + 1
    print(num)
end
for i=1,3,1 do
    f1()--输出1,2,3
end

--f3访问被定义在f2函数中的count
local function f2()         
    local count = 5
    local function f3()
        count = count + 1
        print(count)
    end
    for i=1,3,1 do
        f3()--输出6,7,8
    end
end
f2()

4.闭包应用:(只要出现了使用非局部变量的函数,就用了闭包)

4.1 在返回值是函数的函数中起到作用,比如创建迭代器,示例:

    local function myIpairs(t)
        local index = 0
        local function f1()
            index = index + 1
            return index, t[index]
        end
        return f1
    end
    local color = {"red","blue","yellow","orange","black"}
    local iter = myIpairs(color)
    while true do
        local k,v = iter()
        if v then
            print(k,v)
        else
            break
        end
    end

4.2 作为高阶函数(比如table.sort)的参数,且需要访问外部定义的变量。

    local names = {"Peter", "Paul", "Mary"}
    local grades = {Peter = 8, Paul = 7, Mary = 10}
    for k,v in ipairs(names) do
        print(v)
    end
    table.sort(names, function(n1,n2)
        return grades[n1] > grades[n2]--grades为非局部变量,使用闭包
    end)
    print("--- after sort ---")
    for k,v in ipairs(names) do
        print(v)
    end

4.3 用来重新定义函数,重定义时需要使用原来的函数。

    local str = "galaxy"
    local oldLen = string.len
    function string.len(str)
        local len = oldLen(str)--oldLen为非局部变量,使用闭包
        print(str .. " length: " .. len)
        return len
    end
    local len = string.len(str)--打印出galaxy length: 6

面试环节

问:Lua中闭包的概念是什么?

答:举例来说,有一对有嵌套关系的函数,外包函数叫f1,内嵌函数叫f2,f1内f2外定义一个局部变量a,a叫做f2的非局部变量,每次调用f2时不会重置a的语法特性叫做闭包。闭包可以用来创建迭代器,作为高阶函数的参数以及重新定义已存在的全局函数等作用。

### 闭包的基本概念 在 Lua 中,**闭包(Closure)** 是一个能够访问和记住其法作用域的函数。由于 Lua 将函数视为第一类,函数可以像其他数据类型一样被传递和赋,这使得闭包的创建变得非常自然 [^1]。 从更深层次来看,可以把函数原型理解为“类”,而闭包则是这个类的一个实例。无论何时将函数赋给变量,Lua 都会生成一个新的闭包对象,即使它没有使用任何外部变量(即上 upvalue)。这意味着每次调用都会产生独立的闭包对象 [^2]。 --- ### 闭包的结构与行为 闭包由函数及其捕获的外部环境组成。例如,当一个函数引用了外部作用域中的变量时,该变量被称为 **upvalue**,并被闭包所持有。这种机制允许函数在其定义的作用域之外继续访问这些变量 [^3]。 #### 示例:基本闭包 ```lua function counter() local count = 0 return function() count = count + 1 return count end end local c1 = counter() print(c1()) -- 输出: 1 print(c1()) -- 输出: 2 ``` 在这个例子中,`count` 是 `counter` 函数内部的局部变量,但它被返回的闭包所捕获,并在每次调用时保持状态。 --- ### 闭包的使用场景 闭包广泛用于以下场景: - **回调函数**:在事件驱动编程中,如 GUI 框架或网络请求。 - **封装状态**:实现私有变量和方法。 - **柯里化与部分应用**:通过绑定参数生成新的函数。 - **延迟执行**:将逻辑封装在闭包中,稍后执行。 #### 示例:避免重复创建闭包 为了减少内存开销,可以在高频调用的场景下缓存闭包: ```lua function handler(obj, method) if not obj.__handlerMap then obj.__handlerMap = {} end if obj.__handlerMap[method] == nil then obj.__handlerMap[method] = function(...) return method(obj, ...) end end return obj.__handlerMap[method] end ``` --- ### 闭包的潜在问题 虽然闭包功能强大,但也存在一些需要注意的问题: - **内存泄漏风险**:如果闭包长时间持有对某些变量的引用,可能导致这些变量无法被垃圾回收器释放 [^4]。 - **意外共享变量**:在循环中创建闭包时,所有闭包可能共享同一个变量,而不是各自拥有独立副本 [^5]。 #### 示例:循环中闭包共享变量问题 ```lua local functions = {} for i = 1, 3 do functions[i] = function() return i end end for j = 1, 3 do print(functions[j]()) -- 输出: 4 4 4 end ``` 为了避免这种情况,可以通过引入中间函数来捕获当前的变量: ```lua local functions = {} for i = 1, 3 do functions[i] = (function(x) return function() return x end end)(i) end for j = 1, 3 do print(functions[j]()) -- 输出: 1 2 3 end ``` --- ### 总结 闭包Lua 编程中非常强大的特性,它结合了函数和状态,提供了灵活的控制流和数据封装方式。然而,开发者需要理解其工作机制,特别是在内存管理和变量生命周期方面,以避免潜在的性能问题和逻辑错误 [^1][^2][^3][^4][^5]。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

ellis1970

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值