lua中的闭包

1、在了解lua中的闭包之前,我们先来看一个栗子!!!
这里写图片描述
在上述例子当中,我们用lua的闭包实现了一个计数器。如果之前没有了解过闭包的概念的话,你一定会对上述代码的结果感到奇怪,下面我们就来详细的解释一下闭包。

2、第一类值(First class value )
lua中的函数就是所谓的”第一类值”。
在lua中定义一个函数就像创建普通类型值一样,lua中的函数就是一个值,它可以被存放在变量或数据结构中,可以被当做参数传给另一个函数,可以是另一个函数的返回值,还可以在运行期间被创建。
此外lua中的函数还可以嵌套定义,就是在函数里面定义函数,这也是lua闭包的基础。

3、词法定界
当一个函数内嵌套另一个函数的时候,内嵌套的函数可以访问外包函数的局部变量,这个特征就叫做词法定界。

4、闭包
表层理解闭包概念:
内嵌函数可以使用外包函数的局部变量,这种行为就叫做闭包。也就是词法定界。
深层理解闭包概念:
闭包=函数+upvalue。函数指的就是这些内嵌函数和外包函数。upvalue:内嵌函数可以访问外包函数已创建的所有局部变量,这些局部变量就被称为内嵌函数的upvalue。upvalue实际指的是变量而不是值,这些变量可以在内嵌函数之间共享,就好比C++类中的成员函数可以访问和修改成员变量一样。
可以看到,闭包实际上是数据和行为的结合体,就如同C++中的类一样,在某些情况下,我们需要记录某次调用完成以后数据的状态,就好比C++中的static变量一样,每次调用完成之后static类型的变量并不会被清除。
例:
这里写图片描述
在上述例子当中,upvalue实际上就是参数n,是一个局部变量。我们知道局部变量是保存在堆栈上的,只要upvalue不离开作用域,他就一直生存在函数堆栈上,这种情况下,闭包将使用指向堆栈上的upvalue的引用来访问他们。一但upvalue即将离开作用域,在从堆栈上消失之前,闭包将为upvalue分配新的空间并保存当前upvalue的值,以后便可通过新空间的引用来访问该upvalue。(如果用C语言来解释就是,通过指针访问upvalue这块空间的值,当离开作用域时,重新分配一块空间并将upvalue的值拷贝到里面,然后再让指针指向新分配的空间)
就如同上述例子一样,进入Increment之后闭包创建,这时n还没有离开作用域,所以还是使用堆栈上的n,当return之后离开作用域时,闭包会将n拷贝到自己管理的空间中。

5、闭包和函数的区别
实际上闭包只是在形式和表现上像函数,但实际上不是函数。
我们知道函数是是一些可执行的语句的组合体,这些代码语句在函数被定义后就确定了,并不会在执行时发生改变,所以函数只有一个实例。而闭包在运行时可以有多个实例,不同的upvalue和相同的函数组合可以产生不同的实例,就好像C++中相同的类代码可以创建不同的类实例一样。
例:下面实例和结果如图
这里写图片描述
lua编译一个函数时,其中包含了函数体对应的虚拟机指令、函数用到的常量值和一些调试信息。在运行时,lua每执行一个函数时,他就会创建一个一个新的数据对象,这个数据对象中包含了相应函数原型的引用、环境(用来查找全局变量的表)的引用以及一个由upvalue引用组成的数组,而这个数据对象就被称为闭包。由此可见,函数是编译期间的概念,是静态的,而闭包是运行期间的概念,是动态的。

6、闭包在迭代器中的应用
迭代器需要保留上一次调用的状态和下一次调用成功的状态,我们可以使用闭包机制来实现迭代器。
例:
这里写图片描述
结果:
这里写图片描述

### Lua 闭包的引用机制与工作原理 Lua 中的闭包是一种函数对象,它不仅包含函数本身的指令和常量,还包含了对外部变量(upvalue)的引用。这种结构使得闭包能够在函数执行结束后仍然保持对这些变量的访问能力,从而实现数据的持久化和封装。 在 Lua 编译一个函数时,会生成一个函数原型(function prototype),该原型包括了函数的指令、常量表以及调试信息等[^1]。当 Lua 执行一个函数表达式时,会创建一个新的闭包实例,这个闭包实例包含了对原型的引用和一个 upvalue 数组。从 Lua 5.2 开始,环境的概念被 `_ENV` 变量所取代,而不再是直接关联到闭包上。 例如,在以下代码中,`g` 是一个闭包,它返回并捕获了外部函数 `f` 中定义的局部变量 `a`: ```lua function f() local a = 0 return function g() a = a + 1 print(a) end a = nil -- 主动释放闭包引用到的变量 end local h = f() h() -- 输出 1 h() -- 输出 2 ``` 在这个例子中,尽管 `f` 函数已经执行完毕,但通过返回的闭包 `h`,我们依然可以修改和访问 `a` 的值。这是因为 `a` 被存储在闭包的 upvalue 中,并且只要存在对该闭包的引用,`a` 就不会被垃圾回收器回收。 #### Upvalue 的作用 Upvalue 是指那些在外部函数中定义但在内部函数中使用的局部变量。它们是闭包能够维持状态的关键所在。每个闭包都有自己的 upvalue 表,用于保存这些外部变量的实际引用或拷贝。如果一个局部变量被多个闭包共享使用,则每个闭包都会持有该变量的一个独立副本或者指向同一个内存地址的指针,这取决于具体实现方式。 #### 垃圾回收与闭包生命周期 由于闭包持有了对其外部作用域中变量的引用,因此即使原作用域已经退出,这些变量也不会立即被销毁。这种行为可能导致内存泄漏,特别是当闭包不再需要时没有正确解除对其的引用。为避免这种情况,开发者应该确保在不再需要某些局部变量时显式地将它们设置为 `nil` 或者其他方式解除引用[^2]。 #### 数据封装与模块化 除了影响内存管理之外,闭包还提供了强大的数据封装能力。通过返回一个内部函数,可以让这个函数访问到外部函数的作用域,从而实现私有变量的概念。这种模式有助于创建模块化代码,同时保护内部状态不受全局作用域的影响。 综上所述,Lua闭包的工作原理基于函数原型与 upvalue 结构之间的交互。闭包不仅能够记住其词法作用域,还能维持对外部变量的引用,即使原始作用域已不存在。这种方式支持了复杂的应用逻辑构建,但也需要注意合理使用以防止不必要的内存消耗。 ---
评论 2
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值