当前位置: 首页 > news >正文

Lua 闭包

一、Lua 中的函数

Lua 中的函数是第一类值。意味着和其他的常见类型的值(例如数值和字符串)具有同等权限。

举个例子,函数也可以像其他类型一样存储起来,然后调用

-- 将 a.p 指向 print 函数
a = { p = print }
-- 使用 a.p 函数
a.p("jiangpengyong")    ---> jiangpengyong

二、匿名函数

正常情况下,我们定义一个函数是下面代码这样的

function foo1()print("Foo1 called.")
end

然而,其实可以写成下面这样,将函数赋值给一个变量,这样 foo 就是一个函数类型的变量了。

foo = function(x)return x * x
end

其实 Lua 的函数就是一个 function 类型的变量(可以查看之前的文章“Lua 数据类型 —— 函数”)。第一种方式变量名即为函数名(foo1),第二种则为变量名(foo)

因为函数是一个变量,所以也可以进行判断类型,删除变量等操作。

foo = function(x)return x * x
end
print(foo(2))					-->	4
print("type(foo)", type(foo))	-->	type(foo)	functionfunction foo1()print("Foo1 called.")
end
foo1()							--> Foo1 called.
print("type(foo1)", type(foo1))	--> type(foo1)	function
foo1 = nil
--foo1()    -- attempt to call a nil value (global 'foo1')
print("type(foo1)", type(foo1))	--> type(foo1)	nil

三、高阶函数

以另一个函数作为参数的函数,即为高阶函数。

其实这只是 Lua 函数作为第一类值特性的一个表现,并不是新的特性。

举个例子:

这里编写一个导数函数

f`(x) = (f(x + d) - f(x))/d

编写如下,f 即使一个函数

function derivative(f, delta)delta = delta or 1e-4return function(x)return (f(x + delta) - f(x)) / deltaend
end
c = derivative(math.sin)
print(math.cos(5.2), c(5.2))    --> 0.46851667130038	0.46856084325086

四、在 table 中定义函数

因为函数在 Lua 中与其他类型具有同等权限,所以也可以 table 中定义。

第一种方式,用了匿名函数进行定义,只是归属至表

Lib1 = {}
Lib1.add = function(a, b)return a + b
end
Lib1.reduce = function(a, b)return a - b
end
print("Lib1", Lib1.add(10, 2), Lib1.reduce(2, 3))   --> Lib1	12	-1

第二种方式,也可以使用表构造器的一种方式(记录式)创建

Lib2 = {add = function(a, b)return a + b;end,reduce = function(a, b)return a - b;end
}
print("Lib2", Lib2.add(10, 2), Lib2.reduce(2, 3))   --> Lib2	12	-1

第三种方式,只是用了常规的函数定义

Lib3 = {}
function Lib3.add(a, b)return a + b
end
function Lib3.reduce(a, b)return a - b
end
print("Lib3", Lib3.add(10, 2), Lib3.reduce(2, 3))   --> Lib3	12	-1

五、非全局函数

定义一个局部函数和定义一个局部变量是一样的,例如下面的代码,只需要加上 local 即可

local function fact1(n)if n == 0 thenreturn 1endreturn n * fact1(n - 1)
end
print(fact1(10))    --> 3628800

值得注意

如果用匿名函数定义局部函数的话,则会有坑。

当定义一个递归函数,例如下面这段代码,运行起来会报 attempt to call a nil value (global 'fact2') 错误。

local fact2 = function(n)if n == 0 thenreturn 1end-- 因为 Lua 语言编译函数体中的 fact2(n-1) 调用时,局部的 fact2 尚未定义。return n * fact2(n - 1)    -- attempt to call a nil value (global 'fact2')
end
print(fact2(10))

这是因为 Lua 语言编译函数体中的 fact2(n-1) 调用时,局部的 fact2 还未定义,所以会在全局中进行搜索,所以报错中提示的是 global 'fact2'

所以可以先进行声明然后在使用,就可以避免这一问题。

local fact3
fact3 = function(n)if n == 0 thenreturn 1endreturn n * fact3(n - 1)
end
print(fact3(10))    --> 3628800

所以如果涉及到递归,或者是间接递归,可以考虑先将函数变量声明,然后再进行赋值。

吾有一惑

可能会有疑惑,为什么第一种方式就没有问题?

其实只是 Lua 语言帮我们展开了

local function foo(n) body end-- Lua 帮我们展开为以下代码local foo;
foo = function (n) body end

六、作用域外溢

function newCounter()local count = 0return function()count = count + 1return countend
endlocal c1 = newCounter()
print("c1", c1())       --> c1  1
print("c1", c1())       --> c1  2local c2 = newCounter()
print("c2", c2())       --> c2  1
print("c1", c1())       --> c1  3
print("c2", c2())       --> c2  2
print("c2", c2())       --> c2  3

通过 newCounter 返回一个匿名函数,达到能够 “访问” count , 这就是作用域外溢。

count 的作用域是 newCounter 函数,但是因为作为匿名函数返回,所以外溢至外部。而且每次调用的 local 都不一样。

七、更换预定义函数

Lua 中可以给一个变量重新定义一个新的函数,也可以给一个预定义函数重新定义函数。

例如,我们可以将 sin 函数的参数从原来的 弧度 单位改为 角度 单位。

print("更换预定义函数:")
--- rad 将角度转为弧度
print("更换前,使用弧度制", math.sin(math.rad(90)))     --> 更换前,使用弧度制	1.0
dolocal oldSin = math.sinmath.sin = function(value)return oldSin(value * (math.pi / 180))end
end
print("更换后,使用角度", math.sin(90))                 --> 更换后,使用角度	1.0

使用 do-end 则将 oldSin 的作用域限制起来了,后续的调用只能调用到替换的函数

拓展一下

可以利用这种特性,在原有的函数中增加一些项目所需要的代码,例如日志输出,文件检测等。

八、写在最后

Lua 项目地址:Github传送门 (如果对你有所帮助或喜欢的话,赏个star吧,码字不易,请多多支持)

本章相关代码传送门

如果觉得本篇博文对你有所启发或是解决了困惑,点个赞或关注我呀,后续会分享更多的优质文章。

相关文章:

Lua 闭包

一、Lua 中的函数 Lua 中的函数是第一类值。意味着和其他的常见类型的值(例如数值和字符串)具有同等权限。 举个例子,函数也可以像其他类型一样存储起来,然后调用 -- 将 a.p 指向 print 函数 a { p print } -- 使用 a.p 函数…...

Java技术整理(1)—— JVM篇

1、什么是JVM? JVM是一个可运行Java代码的虚拟计算机,包括一套字节码指令集,一组寄存器,一个栈,一个垃圾回收,堆和一个存储方式栈。JVM 是运行在操作系统之上,并不与操作系统直接交互。 2、运行…...

bug解决:AssertionError: No inf checks were recorded for this optimizer.

这真的是最恶心的一个error(比网络回传找哪层没有传播到还要恶心!),找了好久的问题所在之处,最后偶然发现了这篇文章: 解决pytorch半精度amp训练nan问题 - 知乎 然后发现自己用的混合精度训练,发…...

Django笔记之数据库查询优化汇总

1、性能方面 1. connection.queries 前面我们介绍过 connection.queries 的用法,比如我们执行了一条查询之后,可以通过下面的方式查到我们刚刚的语句和耗时 >>> from django.db import connection >>> connection.queries [{sql: S…...

JVM内存区域

预备 为了更好的理解类加载和垃圾回收,先要了解一下JVM的内存区域(如果没有特殊说明,都是针对的是 HotSpot 虚拟机。)。 Java 源代码文件经过编译器编译后生成字节码文件,然后交给 JVM 的类加载器,加载完…...

某行业CTF一道流量分析题

今晚看了一道题,记录学习下。 给了一个hacktrace.pcapng,分析主要内容如下: 上传两个文件,一个mouse.m2s,一个mimi.zip,将其导出。 mimi.zip中存放着secret.zip和key.pcapng 不过解压需要密码&#xff…...

【Kafka】1.Kafka简介及安装

目 录 1. Kafka的简介1.1 使用场景1.2 基本概念 2. Kafka的安装2.1 下载Kafka的压缩包2.2 解压Kafka的压缩包2.3 启动Kafka服务 1. Kafka的简介 Kafka 是一个分布式、支持分区(partition)、多副本(replica)、基于 zookeeper 协调…...

Kafka API与SpringBoot调用

文章目录 首先需要命令行创建一个名为cities的主题,并且创建该主题的订阅者。 1、使用Kafka原生API1.1、创建spring工程1.2、创建发布者1.3、对生产者的优化1.4、批量发送消息1.5、创建消费者组1.6 消费者同步手动提交1.7、消费者异步手动提交1.8、消费者同异步手动…...

JavaScript构造函数和类的区别

原文 构造函数 没有显式的创建对象创建对象时使用new操作符。所有属性和方法赋值给this对象。没有return语句按照惯例,构造函数的方法名首字母应该使用大写字母,用于区分普通函数,其实构造函数也是函数,其主要功能是用来创建对象…...

Spring与Spring Bean

Spring 原理 它是一个全面的、企业应用开发一站式的解决方案,贯穿表现层、业务层、持久层。但是 Spring 仍然可 以和其他的框架无缝整合。 Spring 特点 轻量级 控制反转 面向切面 容器 框架集合 Spring 核心组件 Spring 总共有十几个组件核心容器(Spring core) S…...

并发相关面试题

巩固基础,砥砺前行 。 只有不断重复,才能做到超越自己。 能坚持把简单的事情做到极致,也是不容易的。 如何理解volatile关键字 在并发领域中,存在三大特性:原子性、有序性、可见性。volatile关键字用来修饰对象的属性…...

Hadoop+Python+Django+Mysql热门旅游景点数据分析系统的设计与实现(包含设计报告)

系统阐述的是使用热门旅游景点数据分析系统的设计与实现,对于Python、B/S结构、MySql进行了较为深入的学习与应用。主要针对系统的设计,描述,实现和分析与测试方面来表明开发的过程。开发中使用了 django框架和MySql数据库技术搭建系统的整体…...

php中nts和ts

PHP语言解析器:官方提供了2种类型的版本,线程安全(TS)版和非线程安全(NTS)版 TS: TS(Thread-Safety)即线程安全,多线程访问时,采用了加锁机制,当一个线程访问该类的某个数据时进行数据加锁保护,其他线程不能同时进行访…...

设计模式之责任链模式【Java实现】

责任链(Chain of Resposibility) 模式 概念 责任链(chain of Resposibility) 模式:为了避免请求发送者与多个请求处理者耦合在一起,于是将所有请求的处理者 通过前一对象记住其下一个对象的引用而连成一条…...

Android 12.0 系统systemui状态栏下拉左滑显示通知栏右滑显示控制中心模块的流程分析

1.前言 在android12.0的系统rom定制化开发中,在系统原生systemui进行自定义下拉状态栏布局的定制的时候,需要在systemui下拉状态栏下滑的时候,根据下滑坐标来 判断当前是滑出通知栏还是滑出控制中心模块,所以就需要根据屏幕宽度,来区分x坐标值为多少是左滑出通知栏或者右…...

服务器安装JDK

三种方法 方法一: 方法二: 首先登录到Oracle官网下载JDK JDK上传到服务器中,记住文件上传的位置是在哪里(我放的位置在/www/java),然后看下面指示进行安装 方法三: 首先登录到Oracle官网下载…...

cpu查询

1.mpstat查看系统cpu状况 mpstat 1 1或者mpstat -P ALL查看每个cpu使用状态,(用户态cpu是用来,内核态cpu使用率,等待IO使用率) 2.vmstat 可以查看系统运行任务数(正在cpu运行进程和就绪队列进程&#xff0…...

【muduo】关于自动增长的缓冲区

目录 为什么需要缓冲区自动增长的缓冲区buffer数据结构buffer类 写详细比较费时间,就简单总结下。 总结自Linux 多线程服务端编程:使用 muduo C 网络库 Muduo网络编程: IO-multiplexnon-blocking 为什么需要缓冲区 Non-blocking IO 的核心…...

原型和原型链理解

这个图大概能概括原型和原型链的关系 1.对象都是通过 _proto_ 访问原型 2.原型都是通过constructor 访问构造函数 3.原型是构造函数的 prototype 4.原型也是对象实例 也是通过 _proto_ 访问原型(Object.prototype) 5.Object.prototype的原型通过 _proto_ 访问 为null 那么…...

CSS:弹性盒子模型详解(用法 + 例子 + 效果)

目录 弹性盒子模型flex-direction 排列方式 主轴方向换行排序控制子元素缩放比例缩放是如何实现的? 控制子元素的对其方式justify-content 横向 对齐方式align-items 纵向 对齐方式 align-content 多行 对齐方式 弹性盒子模型 flex-direction 排列方式 主轴方向 f…...

wordpress后台更新后 前端没变化的解决方法

使用siteground主机的wordpress网站,会出现更新了网站内容和修改了php模板文件、js文件、css文件、图片文件后,网站没有变化的情况。 不熟悉siteground主机的新手,遇到这个问题,就很抓狂,明明是哪都没操作错误&#x…...

测试markdown--肇兴

day1: 1、去程:7:04 --11:32高铁 高铁右转上售票大厅2楼,穿过候车厅下一楼,上大巴车 ¥10/人 **2、到达:**12点多到达寨子,买门票,美团/抖音:¥78人 3、中饭&a…...

在四层代理中还原真实客户端ngx_stream_realip_module

一、模块原理与价值 PROXY Protocol 回溯 第三方负载均衡(如 HAProxy、AWS NLB、阿里 SLB)发起上游连接时,将真实客户端 IP/Port 写入 PROXY Protocol v1/v2 头。Stream 层接收到头部后,ngx_stream_realip_module 从中提取原始信息…...

SpringTask-03.入门案例

一.入门案例 启动类: package com.sky;import lombok.extern.slf4j.Slf4j; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cache.annotation.EnableCach…...

VM虚拟机网络配置(ubuntu24桥接模式):配置静态IP

编辑-虚拟网络编辑器-更改设置 选择桥接模式,然后找到相应的网卡(可以查看自己本机的网络连接) windows连接的网络点击查看属性 编辑虚拟机设置更改网络配置,选择刚才配置的桥接模式 静态ip设置: 我用的ubuntu24桌…...

C语言中提供的第三方库之哈希表实现

一. 简介 前面一篇文章简单学习了C语言中第三方库(uthash库)提供对哈希表的操作,文章如下: C语言中提供的第三方库uthash常用接口-CSDN博客 本文简单学习一下第三方库 uthash库对哈希表的操作。 二. uthash库哈希表操作示例 u…...

LangFlow技术架构分析

🔧 LangFlow 的可视化技术栈 前端节点编辑器 底层框架:基于 (一个现代化的 React 节点绘图库) 功能: 拖拽式构建 LangGraph 状态机 实时连线定义节点依赖关系 可视化调试循环和分支逻辑 与 LangGraph 的深…...

MFE(微前端) Module Federation:Webpack.config.js文件中每个属性的含义解释

以Module Federation 插件详为例,Webpack.config.js它可能的配置和含义如下: 前言 Module Federation 的Webpack.config.js核心配置包括: name filename(定义应用标识) remotes(引用远程模块&#xff0…...

ubuntu22.04 安装docker 和docker-compose

首先你要确保没有docker环境或者使用命令删掉docker sudo apt-get remove docker docker-engine docker.io containerd runc安装docker 更新软件环境 sudo apt update sudo apt upgrade下载docker依赖和GPG 密钥 # 依赖 apt-get install ca-certificates curl gnupg lsb-rel…...

el-amap-bezier-curve运用及线弧度设置

文章目录 简介示例线弧度属性主要弧度相关属性其他相关样式属性完整示例链接简介 ‌el-amap-bezier-curve 是 Vue-Amap 组件库中的一个组件,用于在 高德地图 上绘制贝塞尔曲线。‌ 基本用法属性path定义曲线的路径,可以是多个弧线段的组合。stroke-weight线条的宽度。stroke…...