Lua闭包和Upvalue上值

news2024/12/24 15:55:16

一、lua中的作用域

在Lua语言中声明的变量默认是全局变量,声明局部变量需要使用local关键字,和其他语言相比这有点特殊。

-- 全局变量
a = 10

function func()
    b = 100   -- 仍然是全局变量
    local c = 20  -- func的局部变量
end
func()

print(a + b)  -- 输出 110
print(c)    -- 输出 nil

运行结果:
在这里插入图片描述

二、什么是闭包

我们知道,函数里的本地变量只能在函数内部访问,函数退出之后,作用域就没用了,它对应的栈桢被弹出,作用域中的所有变量所占用的内存也会被收回。

闭包的功能是让一个函数的变量逃不出去,什么意思?意思是当一个函数被当做成是一个返回值时,这个函数里所有引用的变量都不会丢失,不会因为离开了一个变量离开了作用域而导致创建或者使用这个函数时,其变量值会发生变化。

当一个函数内部嵌套另一个函数定义时,内部的函数可以访问外部的函数的局部变量,这种特征在lua中叫:词法定界

lambda表达式就是经典的闭包方式之一。

简单来说,闭包就是能够读取其他函数内部变量的函数。

三、闭包的实现

要实现闭包,需要解决两个问题:

函数是第一类值(first class value),也就是要能把函数像普通数值一样赋值给变量,可以作为参数传递给其他函数,可以作为函数的返回值。
满足词法作用域(Lexical Scope),要让内层函数能够访问外层函数中的变量,不管外层函数退出与否。
在Lua语言中,函数是严格遵循词法作用域的第一类值。具备了实现闭包的条件,下面通过一个例子说明什么是闭包。

例子中,定义了一个全局变量a,和func函数,并且在func函数内部定义了局部变量b。func函数中内嵌并返回了一个test函数,test返回的是全局变量和func的局部变量,我们来看看程序输出结果:

a = 10

function func()

    local b = 20;

    function test()
        a = a + 10;
        b = b + 20;
        -- 返回全局变量a和局部变量b
        return a, b
    end

    return test
end

-- tmpFunc1 就是一个闭包
tmpFunc1 = func()

for i=1, 3 do
    v1, v2 = tmpFunc1()
    print(v1, v2)
end

运行结果:
在这里插入图片描述
分析:
内层函数test作为返回值赋值给tmpFunc1 变量后,外层函数func就结束了,但内层函数仍能访问原来外层函数的变量 b,当然,也能访问全局变量 a。

站在func的角度来看,自己已经退出了,那么自己定义的局部变量b就应该失效了,但是,闭包tmpFunc1仍然可以访问,这就是闭包。

四、Upvalue 上值

内嵌函数可以访问外包函数已经创建的局部变量,而这些局部变量则称为该内嵌函数的upvalue,也就是上值。

对于上面的例子,局部变量b就是内嵌函数test的上值。注意,函数的参数也是局部变量,看下面的例子:

function create_func(n)
    function test()
        n = n + 100;
        return n
    end
    return test
end

func1 = create_func(1000)
print(func1())

func2 = create_func(50)
print(func2())

运行结果
在这里插入图片描述
分析:
当 func1 = create_func(1000) 这行代码执行完,局部变量n的生命周期本该结束,但是,因为n已经成为了内嵌函数test的上值,以某种方式“存活”在闭包func1中。

4.1 Upvalue为闭包提供数据共享

4.1.1 函数创建的闭包

一个函数创建的闭包共享一份upvalue。

function create_func(n)
    function test1()
        n = n + 100;
        print(n)
    end
    function test2()
        print(n)
    end
    return test1, test2
end

func1,func2 = create_func(1000)

func1()
func2()
print("")
func1()
func2()

运行结果:
在这里插入图片描述
结果分析
func1 和 func2 这两个闭包的函数原型分别是 create_func 中的内嵌函数test1和test2,而 test1 和 test2 引用的upvalue是同一个,即create_func 的形参n(局部变量)。

执行完create_func调用后,Lua发现这两个闭包的upvalue指向的是当前堆栈上的相同变量时,只生成一个拷贝,然后让这两个闭包共享该拷贝,这样任一个闭包对该upvalue进行修改都会被另一个知悉。

可以看到,每次调用 func1都将upvalue的值增加了100,随后func2将更新后的值打印出来。upvalue的这种语义很有价值,它使得闭包之间可以不依赖全局变量进行通讯,从而使代码的可靠性大大提高。

4.1.2 闭包创建的闭包(多重内嵌)
function create_func(n)

    function test ()
        function test1()
            n = n + 100;
            print(n)
        end

        function test2()
            print(n)
        end

        return test1, test2
    end

    return test
end


func = create_func(1000)

func1, func2 = func()
func1()
func2()
print("")
func1()
func2()
print("====================")
func3, func4 = func()
func3()
func4()
print("")
func3()
func4()

运行结果:
在这里插入图片描述
结果分析
在执行完 func = create_func(1000) ,create_func 的局部变量 n 的生命周期就结束了,所以当func1,func2这两个闭包被创建时堆栈上根本找不到变量 n。create_func 函数的局部变量n不仅是 test 的 upvalue,也是test1和test2的upvalue。闭包 func 已经把n保存为upvalue,之后func1、func2如果在当前堆栈上找不到变量n就会自动到它们的外包闭包(这里指的是闭包func)的upvalue引用数组中去找.
  
func3和func4与func和func2共享同一个上值upvalue。因为func3和func4与func和func2都是同一个闭包 func 创建的,所以它们引用的upvalue (变量n)实际也是同一个变量,而它们的upvalue引用都会指向同一个地方。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/126219.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

终极.NET混淆器丨.NET Reactor产品介绍

无与伦比的 .NET 代码保护系统,可完全阻止任何人反编译您的代码。 产品优势 01、混淆技术 .NET Reactor通过向 .NET 程序集添加不同的保护层来防止逆向工程。除了标准的混淆技术之外,它还包括NecroBit、虚拟化、x86代码生成或防篡改等特殊功能。NET Re…

xilinx srio ip学习笔记之初识srio

提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档 xilinx srio ip学习笔记之初识srio前言IP 设置总结前言 因为工作原因,需要对rapidio 的协议进行了解,在xilinx的IP核中,是对应着Serial R…

这支隐藏“球队”,颠覆消费品「赛场」

【潮汐商业评论/原创】 大好的黄金周末,Fred约了几个朋友来家里看球。按照他的计划,周五准备下班后,他赶紧得去一趟附近的大型超市扫货,买一批零食酒水招待朋友。没想到的是,好不容易等到快下班了,领导通知…

外包呆一年,外包的工作经历怎么写?外包的项目经验怎么写?

0. 先来看下大家的各种问题? 外包的工作经历怎么写?外包的项目经验怎么写?外包如何优化简历?进入外包后黑化了简历,如何成功跳出外包圈?外包该如何提升自己?外包仔如何自我救赎? ……

前端基础_离线Web应用概述

离线Web应用概述 在Web应用中使用缓存的原因之一是为了支持离线应用。在全球互联的时代,离线应用仍有其实用价值。当无法上网的时候,你会做什么呢?你可能会说如今网络无处不在,而且非常稳定,不存在没有网络的情况。但…

【服务器数据恢复】误操作导致ocfs2文件系统被格式化的数据恢复案例

服务器故障: 用户误操作将linux文件系统误装入到Ocfs2文件系统的数据卷上,导致原始Ocfs2文件系统被格式化为Ext4文件系统。 因为Ext4文件系统每隔几百兆就会写入文件系统的原始信息,所以本案例中的原始Ocfs2文件系统中的数据可能受到一定程度…

搭建开源版个人图床

在微博图床、gitee、jsDelivr 陆续被 ban 的今天,很有必要搭建自己的图床系统了。 兰空图床 兰空图床官网:https://www.lsky.pro docker版本:https://hub.docker.com/r/halcyonazure/lsky-pro-docker 本次讲解使用 docker 版本进行部署使用 …

linux跟踪技术之ebpf

ebpf简介 eBPF是一项革命性的技术,起源于 Linux 内核,可以在操作系统内核等特权上下文中运行沙盒程序。它可以安全有效地扩展内核的功能,而无需更改内核源代码或加载内核模块。 比如,使用ebpf可以追踪任何内核导出函数的参数&…

漫画电学原理

电是什么 电压 电压是两点的电势差。 电流是指每秒在导线中流动的电量。 电功率是指在1s内消耗的电能。 电的本质是什么 万物都是有原子构成,原子有原子核(正电),核外电子(负电)构成。电子的定向移动形成了电。 电子离开原子,原子的电子减少,从而带正电。带正电的…

互联网时代“陨落”,国家发布元宇宙战略的信号对失业和担心失业的我们带来了什么启迪?

互联网这头“猪 ”真的掉下来了 流量红利已经一去不复返了!3年前业界其实已经发出各种密集信号,在当时无论是BAT还是一些经济学家在3年前都已经预测过,互联网的流量模式已经衰竭,并且它将一去不复返。 曾经处于互联网大潮的我们…

day10Git

1.Git介绍 1.1版本控制(理解) 无论是代码编写,还是文档编写,我们都会遇到对文档内容反复修改的情况 1.2开发中存在的问题(理解) 程序员小明负责的模块就要完成了,就在即将提交发布之前的一瞬间,电脑突然蓝屏,硬盘光…

HTC FOCUS 3连接FOHEART H1数据手套

本教程介绍使用H1数据手套与HTC腕带式追踪器驱动VR中的虚拟手运动,实现手部的追踪及定位。 需要准备的硬件: 1、FOHEART H1数据手套 2、HTC VIVE Focus 3一体机 3、HTC VIVE 腕带式追踪器 01 一体机连接腕带追踪器 首先断开Focus3的手柄&#x…

【JavaWeb】Mybatis深度进阶练习

学习目标 能够使用映射配置文件实现CRUD操作能够使用注解实现CRUD操作 文章目录1、配置文件实现CRUD1.1 环境准备1.2 查询所有数据1.2.1 编写接口方法1.2.2 编写SQL语句1.2.3 编写测试方法1.2.4 起别名解决上述问题1.2.5 使用resultMap解决上述问题1.2.6 小结1.3 查询详情1.3.1…

(一) 初识python

1. python的特点: 可读性强 可读性远比听上去重要的多得多。一个程序会被反复的修改,可读性意味这让你可以在更短时间内学习和记忆,直接提高生产率。高效、简洁 研究证明,程序员每天可编写的有效代码是有限的。完成同样功能只用一…

浙大MBA复试经验分享——复试备考流程及要点提醒

最近身边有很多小伙伴都在咨询关于复试的问题,趁着今天有空把我的复试准备过程按照时间线梳理出来分享给大家,希望可以帮助到大家。 联考结束后,趁着考完印象还深刻,我们首先要做的就是估分(辅导班或者小红书、知…

【06】概率图推断之变量消除算法

概率图推断之变量消除算法 文章目录说明性示例消除变量因子因子运算排序变量消除算法举例证据变量消除的时间复杂度选择变量消除顺序接下来,我们将注意力转向图模型中的推断问题。 给定概率模型(如贝叶斯网络或马尔可夫随机场),…

开个脑洞,带你写一个自己的极狐GitLab CI Runner

极狐GitLab Runner 是极狐GitLab CI/CD 执行的利器,能够帮助完成 CI/CD Pipeline Job 的执行。 目前极狐GitLab Runner 是一个开源项目,以 Golang 编写。 极狐Gitlab 有个不错的特性,就是你可以使用自己的极狐Gitlab CI Runner。可是&#xf…

Oracle 单实例如何开机自启动

作者 | JiekeXu来源 |公众号 JiekeXu DBA之路(ID: JiekeXu_IT)如需转载请联系授权 | (个人微信 ID:JiekeXu_DBA)大家好,我是 JiekeXu,很高兴又和大家见面了,今天和大家一起来看看 Oracle 单实例如何开机自启动,欢迎点击…

盘点JAVA程序猿必备的webserver

作为java工程师,除了必备的java编程能力,我们还需要些什么呢? 一般而言,要从工程师进化为构架师,一个合格的java工作者需要掌握一些关于构架的知识, 比如互联网的结构,服务器的建设&#xff0c…

PhotoShop入门

PhotoShop入门 零、文章目录 文章地址 个人博客-CSDN地址:https://blog.csdn.net/liyou123456789个人博客-GiteePages:https://bluecusliyou.gitee.io/techlearn 代码仓库地址 Gitee:https://gitee.com/bluecusliyou/TechLearnGithub&am…