Lua再学习

news2025/5/14 3:35:14

因为实习的项目用到了Lua,所以再来深入学习一下

函数

函数的的多返回值

Lua中的函数可以实现多返回值,实现方法是再return后列出要返回的值的列表,返回值也可以通过变量接收到,变量不够也不会影响接收对应位置的返回值

Lua中传入参数和参数个数不匹配也不会报错,只会补空或丢弃

函数的重载

在Lua中他是不支持函数的重载的,默认调用最后声明的这个函数,但是使用重载的方法也不会报错

变长参数

变长参数在函数内使用时需要先用一个表存起来,然后再去操作

function add(...)  
local s = 0  
  for i, v in ipairs{...} do   --> {...} 表示一个由所有变长参数构成的数组  
    s = s + v  
  end  
  return s  
end  
print(add(3,4,5,6,7))  --->25

函数嵌套

function F8(x)
	return function(y)//不需要命名
		return x+y
	end
end
f9=F8(10)//相当于返回一个函数类型的变量
print(f9(5))//15

这也是Lua中闭包的体现
改变传入参数的生命周期
传入参数x本应在执行完之后被销毁,但因为闭包改变了x的生命周期让其可以持续存在

table

lua中所有的复杂类型都是table,数组二维数组字典类都是用table实现的
数组
lua中索引从1开始
a={1,2,4,“123”}
#是通用的获取长度的关键字
如果表中某一位为nil会影响#获取长度(5.1已改),长度从nil开始截断
二维数组

a={{1,2,3},{4,5,6}}
for i=1,#a do
	b=a[i]
	for j=1,#b do
		print(b[j])
	end
end

table实现类

lua中复杂一点的数据结构都是通过类来实现的
lua中没有面向对象的概念,需要我们自己来实现

Student={
	age=1,
	sex=true,
	Up=function()
	//函数体
	//在表内部的函数调用表内部的变量必须要指定是谁的
		print(Student.age)
	end,
	Learn=function(t)
	//把自己作为参数传进来
		print(t.age)
	//函数体
	end
}
//不像C#一样需要new一个对象来实现调用
Student.Up()
//可以在表外申明表的变量和方法
Student.name="111"
Student.Speak=function()
end
function Student.Speak2()
end
//冒号会把调用者作为第一个参数传入
//如果使用冒号申明函数,相当于有一个默认参数
Student:Learn()
Student.Learn(Student)

协程

协程的创建
协程的创建一般依附于一个函数
协程的本质是一个线程对象

--常用方式
--返回的是一个线程
fun=function()
end
co=coroutine.create(fun)
--返回的是一个函数
co2=coroutine.wrap(fun)

协程的运行
两种创建方式对应的运行方式也不同

coroutine.resume(co)
--因为协程返回的是一个函数,所以可以直接运行
co2()

协程的挂起

fun2=function()
	while ture do
		print("123")
		--挂起函数
		coroutine.yield()
		//协程也可以返回值
		--coroutinre.yield(i)
	end
end
co3=coroutine.create(fun2)
--区别于C#的协程,lua中的协程每调用一次执行一次对应的函数
coroutine.resume(co3)
-- 默认第一个返回值是协程是否启动成功

--这种方式的协程调用也可以有返回值,只是没有默认第一个返回值了
co4=coroutine.wrap(fun2)
co4()
co4()

协程的状态
dead结束
running运行
suspended暂停

coroutine.status(协程对象)
--获取正在运行的协程的协程号
coroutine.running()

元表

元表概念
任何表变量都可以作为另一个表变量的元表
任何表变量都可以有自己的元表
当我们子表(有元表的表)进行一定操作时会执行元表中的内容

设置元表

mete={}
mytable={}
--设置元表函数,第一个参数子表,第二个参数元表
setmetatable(mete,mytable)
getmetatable(mete)--获得子表对应的元表

元表的特定操作

__tostring(用的多)

mete={
	--当子表要作为字符串使用时,会默认调用元表中的__tostring 方法
	__tostring=function(t)
		return t.name
	end
}
mytable={
	name="123"
}
setmetatable(mete,mytable)
print(mytable)--输出123

__call(用的多)

mete={
	--当子表被当作一个函数来使用时,会默认调用这个__call中的内容
	--当希望传参数时第一个参数一定是调用者自己
	__call=function(a,b)
		print(a)
		print(b)
	end
}
mytable={
	name="123"
}
setmetatable(mete,mytable)
print(mytable)
--输出123
1

运算符重载(用的可能不多)

mete={
	--相当于运算符重载 当子表使用对应运算符时会调用该方法
	-- +
	__add=function(a,b)
		return a.name+b.name
	end,
	-- ==调用的两个表的元表必须一致,才能准确调用此方法
	__eq=function(a,b)
		return true
	end
}
mytable={
	name=1
}
mytable2={
	name=2
}
setmetatable(mete,mytable)
print(mytable+mytable2)
--输出123
1

__index

mete={
	
}
meta.__index={age=2}
-- __index的赋值写在表外面来初始化,和C++的构造函数不可以是虚函数一个道理,自己都没有初始化好要怎么指向自己内部的东西呢
mytable={
	
}
setmetatable(mete,mytable)
__index:当子表中找不到某一个属性时,会到元表中__index指定的表去找属性
print(mytable.age)--输出2

__index还可以实现“套娃”,当子表中找不到某一个属性时,会到元表中找这个属性,如果元表中也找不到该属性则会到元表的元表中去寻找

__newIndex

__newIndex当赋值时,如果赋值一个不存在的索引,那么会把这个值赋值到__newIndex所指的表中,不会修改自己

mete={
	
}
meta.__newIndex={}
mytable={
}
setmetatable(mete,mytable)
mytable.age=1
print(mytable.age)--输出2

使用rawget和rawset可以直接设置对应的表,而非索引指向的元表

实现面向对象

表就是表现类的一种形式

在Lua中,冒号语法糖用于简化方法的定义和调用,自动传递self参数。当用冒号定义方法时,实际上隐式地添加了self作为第一个参数。例如,obj:method() 转换成 obj.method(obj)
如果用点号调用时冒号声明的方法,需要显式传递self。

封装

声明对象实际上是声明了一张空表,获取属性是通过设置元表后获取元表的属性实现的

--self代表的是我们默认传入的第一个参数
--对象就是变量,返回一个新的变量
--返回出去的内容本质是一个表对象

Object={}
Object.id=1
function Object:Test()
	print(self.id)
end

function Object:new()
	local obj={}
	--当子表中找不到某一个属性时,会到元表中__index指定的表去找属性
	self.__index=self
	setmetatable(obj,self)
	return obj
end

local myobj=Object:new()
myobj:Test()--输出1
对空表中申明一个新的属性叫做id
myobj.id=2
myobj:Test()--输出2

继承

接上文
_G来根据字符串创建一个新的表(类)
在这里插入图片描述

function Object:subClass(classNmae)
	-- _G是总表 所有声明的全局标量 都以键值对的形式存在在其中
	_G[className]={}
	local obj=_G[className]
	self.__index=self
	setmetatable(obj,self)
end
Object:subClass("Person")
local p1=Person:new()
print(p1.id)--输出1

多态

多态的本质是相同的方法执行不同的逻辑

代码接上文
方法1:子类直接重写这个方法
方法2:通过给子类添加base属性保留父类逻辑执行

function Object:subClass(classNmae)
	-- _G是总表 所有声明的全局标量 都以键值对的形式存在在其中
	_G[className]={}
	local obj=_G[className]
	self.__index=self
	-- 为子类定义base属性 base属性代表父类
	obj.base=self
	setmetatable(obj,self)
end

Object:subClass("GameObject")
GameObject.PosX=0
GameObject.PosY=0

function GameObject:Move()
	self.PosX=self.PosX+1
	self.PosY=self.PosY+1
end
Object:subClass("Player")

function Player:Move()
-- base指的是GameObject表()
-- 这种调用方式相当于是把基类表作为第一个参数传入了方法中
	self.base:Move()
end

local p1=Player:new()
p1:Move()
local p2=Player:new()
p2:Move()

目前这种写法有坑,不同对象使用的成员变量是相同的成员变量,不是自己的

更正版

function Player:Move()
-- 如果我们要执行父类逻辑,则需要避免将父类传入到方法中
-- 所以要使用.去调用,自己传入第一个参数
	self.base.Move(self)
end

总结

Object={}
Object.id=1
function Object:new()
	local obj={}
	--给空对象设置元表和__index
	self.__index=self
	setmetatable(obj,self)
	return obj
end

function Object:subClass(classNmae)
	-- 根据名字生成一张表,即一个类
	_G[className]={}
	local obj=_G[className]
	--给子类设置元表以及__index
	self.__index=self
	--设置自己的父类
	obj.base=self
	setmetatable(obj,self)
end

--申明一个新的类
Object:subClass("GameObject")
--成员变量
GameObject.PosX=0
GameObject.PosY=0
--成员方法
function GameObject:Move()
	self.PosX=self.PosX+1
	self.PosY=self.PosY+1
end

-- 实例化对象使用
local obj=GameObject:new()
obj:Move()

--申明一个新的类,Player继承GameObject
Object:subClass("Player")

--重写了Move方法
function Player:Move()
--base调用父类方法,自己传第一个参数
	self.base.Move(self)
end

local p1=Player:new()
p1:Move()

垃圾回收

collectgarbage

test={id=1}
-- lua中的机制和垃圾回收很类似,置空之后就认定为垃圾
test=nil
--进行垃圾回收,理解有点像C#的GC
collectgarbage("collect")
--获取当前lua占用内存数
collectgarbage("count")

lua中有自动定时进行GC的方法
但是在热更新开发中通常不会使用自动GC,而是选择在内存占用量达到一定程度时手动GC,减少性能损耗

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

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

相关文章

GitLab搭建与使用(SSH和Docker)两种方式

前言 目前公共的代码仓库有很多,比如:git、gitee等等仓库但是我们在公司中,还是要搭建属于本公司自己的一个代码仓库,原因有如下几点 代码私密性,我们公司开发的代码保密性肯定一级重要,那么我们放到公网上&#xff0c…

Linux数据库篇、第零章_MySQL30周年庆典活动

MySQL考试报名网站 Oracle Training and Certification | Oracle 中国 活动时间 2025年 MySQL的30周年庆典将于2025年举行。MySQL于1995年首次发布,因此其30周年纪念日是2025年。为了庆祝这一里程碑,MySQL将提供免费的课程和认证考试,活动…

Windows ABBYY FineReader 16 Corporate 文档转换、PDF编辑和文档比较

作为一名合格的工人,日常工作肯定离不开PDF文件,所以今天给大家找来了一款全新的PDF处理工具,保证能给你带来不一样的体验。 软件介绍 这是一个全能型的PDF处理器,集优秀的文档转换、PDF编辑和文档比较等功能于一身,…

【每日一题 | 2025年5.5 ~ 5.11】搜索相关题

个人主页:Guiat 归属专栏:每日一题 文章目录 1. 【5.5】P3717 [AHOI2017初中组] cover2. 【5.6】P1897 电梯里的尴尬3. 【5.7】P2689 东南西北4. 【5.8】P1145 约瑟夫5. 【5.9】P1088 [NOIP 2004 普及组] 火星人6. 【5.10】P1164 小A点菜7. 【5.11】P101…

多模态大语言模型arxiv论文略读(六十九)

Prompt-Aware Adapter: Towards Learning Adaptive Visual Tokens for Multimodal Large Language Models ➡️ 论文标题:Prompt-Aware Adapter: Towards Learning Adaptive Visual Tokens for Multimodal Large Language Models ➡️ 论文作者:Yue Zha…

精讲C++四大核心特性:内联函数加速原理、auto智能推导、范围for循环与空指针进阶

前引:在C语言长达三十余年的演进历程中,每一次标准更新都在试图平衡性能与抽象、控制与安全之间的微妙关系。从C11引入的"现代C"范式开始,开发者得以在保留底层控制能力的同时,借助语言特性大幅提升代码的可维护性与安全…

【HarmonyOS 5】鸿蒙中常见的标题栏布局方案

【HarmonyOS 5】鸿蒙中常见的标题栏布局方案 一、问题背景: 鸿蒙中常见的标题栏:矩形区域,左边是返回按钮,右边是问号帮助按钮,中间是标题文字。 那有几种布局方式,分别怎么布局呢?常见的思维…

Day22 Kaggle泰坦尼克号训练实战

​ 作业 自行学习参考如何使用kaggle平台,写下使用注意点,并对下述比赛提交代码 kaggle泰坦里克号人员生还预测 一、流程 思路概述 数据加载 :读取泰坦尼克号的训练集和测试集。数据预处理 :处理缺失值、对分类变量进行编码、…

深入浅出之STL源码分析7_模版实例化与全特化

1.引言 显示实例话和全特化的区别,之前我们在讨论类模版的时候,讨论过,他俩不是同一个概念,类模版中你如果全特化了,还是需要实例化才能生成代码,但是对于函数模版,这个是不同的,函…

2025 Mac常用软件安装配置

1、homebrew 2、jdk 1、使用brew安装jdk: brew install adoptopenjdk/openjdk/adoptopenjdk8 jdk默认安装位置在 /Library/Java/JavaVirtualMachines/adoptopenjdk-8.jdk/Contents/Home 目录。 2、配置环境变量: vim ~/.zshrc# Jdk export JAVA_HOM…

容器技术 20 年:颠覆、重构与重塑软件世界的力量

目录 容器技术发展史 虚拟化技术向容器技术转变 Docker的横空出世 容器编排技术与Kubernetes 微服务的出现与Istio 工业标准的容器运行时 容器技术与 DevOps 的深度融合​ 无服务架构推波助澜 展望未来发展方向 从 20 世纪硬件虚拟化的笨重,到操作系统虚拟…

cmake:test project

本文主要探讨cmake在测试和项目中的应用。 add_test add_test(NAME <name> COMMAND <command> [<arg>...] [CONFIGURATIONS <config>...] [WORKING_DIRECTORY <dir>] [COMMAND_EXPAND_LISTS])  add_test(NAME test_uni COMMAND $<TARGET_F…

C++开发过程中的注意事项详解

目录 C++开发过程中的注意事项详解 一、内存管理:避免泄漏与资源浪费 1.1 使用智能指针管理动态内存 1.2 避免手动内存管理的陷阱 1.3 利用RAII机制管理资源 1.4 容器与内存分配 二、安全性:防御攻击与未定义行为 2.1 输入验证与安全编码 2.2 使用安全的通信协议 2…

第六天:Java数组

数组 数组概述 数组是相同类型数据的有序集合。数组中的元素可以是任意数据类型&#xff0c;包括基本类型和引用类型数组描述是相同类型的若干个数据&#xff0c;按照一定的先后顺序排列组合而成。数组下标从0开始。 数组声明与创建 数组的声明 int[] nums;//声明一个数组…

李沐动手深度学习(pycharm中运行笔记)——09.softmax回归+图像分类数据集+从零实现+简洁实现

09.softmax回归图像分类数据集从零实现简洁实现&#xff08;与课程对应&#xff09; 目录 一、softmax回归 1、回归 vs 分类 2、经典分类数据集&#xff1a; 3、从回归到分类——均方损失 4、从回归到多类分类——无校验比例 5、从回归到多类分类——校验比例 6、softmax和…

Qt获取CPU使用率及内存占用大小

Qt 获取 CPU 使用率及内存占用大小 文章目录 Qt 获取 CPU 使用率及内存占用大小一、简介二、关键函数2.1 获取当前运行程序pid2.2 通过pid获取运行时间2.3 通过pid获取内存大小 三、具体实现五、写在最后 ​ 一、简介 近期在使用软件的过程中发现一个有意思的东西。如下所示&a…

嵌入式学习笔记 - 运算放大器的共模抑制比

一 定义 共模抑制比&#xff08;Common Mode Rejection Ratio, ‌CMRR‌&#xff09;是衡量差分放大器&#xff08;或差分电路&#xff09;抑制共模信号能力的关键指标。它在电子工程中尤为重要&#xff0c;特别是在需要处理微弱信号或对抗环境噪声的场景中。 核心概念 ‌共…

牛客周赛 Round 92-题解

牛客周赛 Round 92-题解 A-小红的签到题 code #include<iostream> #include<string> using namespace std; string s; int main() {int n;cin >> n;cout << "a_";for (int i 0; i < n - 2; i )cout << b;return 0; }B-小红的模…

【PVE】ProxmoxVE8虚拟机,存储管理(host磁盘扩容,qcow2/vmdk导入vm,vm磁盘导出与迁移等)

【PVE】ProxmoxVE8虚拟机&#xff0c;存储管理&#xff08;host磁盘扩容&#xff0c;qcow2/vmdk导入vm&#xff0c;vm磁盘导出与迁移等&#xff09; 文章目录 1、host 磁盘扩容2、qcow2/vmdk导入vm3、vm 磁盘导出与迁移 1、host 磁盘扩容 如何给host扩容磁盘&#xff0c;如增加…

Umi+React+Xrender+Hsf项目开发总结

一、菜单路由配置 1.umirc.ts 中的路由配置 .umirc.ts 文件是 UmiJS 框架中的一个配置文件&#xff0c;用于配置应用的全局设置&#xff0c;包括但不限于路由、插件、样式等。 import { defineConfig } from umi; import config from ./def/config;export default defineCon…