欢迎来到尧图网

客户服务 关于我们

您的位置:首页 > 健康 > 养生 > Lua再学习

Lua再学习

2025/5/13 15:38:47 来源:https://blog.csdn.net/Petrichorzncu/article/details/147742912  浏览:    关键词:Lua再学习

因为实习的项目用到了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+yend
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 dob=a[i]for j=1,#b doprint(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 doprint("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.nameend
}
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.nameend,-- ==调用的两个表的元表必须一致,才能准确调用此方法__eq=function(a,b)return trueend
}
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)
endfunction Object:new()local obj={}--当子表中找不到某一个属性时,会到元表中__index指定的表去找属性self.__index=selfsetmetatable(obj,self)return obj
endlocal 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=selfsetmetatable(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=selfsetmetatable(obj,self)
endObject:subClass("GameObject")
GameObject.PosX=0
GameObject.PosY=0function GameObject:Move()self.PosX=self.PosX+1self.PosY=self.PosY+1
end
Object:subClass("Player")function Player:Move()
-- base指的是GameObject表()
-- 这种调用方式相当于是把基类表作为第一个参数传入了方法中self.base:Move()
endlocal 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={}--给空对象设置元表和__indexself.__index=selfsetmetatable(obj,self)return obj
endfunction Object:subClass(classNmae)-- 根据名字生成一张表,即一个类_G[className]={}local obj=_G[className]--给子类设置元表以及__indexself.__index=self--设置自己的父类obj.base=selfsetmetatable(obj,self)
end--申明一个新的类
Object:subClass("GameObject")
--成员变量
GameObject.PosX=0
GameObject.PosY=0
--成员方法
function GameObject:Move()self.PosX=self.PosX+1self.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)
endlocal p1=Player:new()
p1:Move()

垃圾回收

collectgarbage

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

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

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com

热搜词