Lua | 一篇文章讲清Lua语法及热更新

目录

一、环境搭建

二、Lua语法

1.输出print、单行注释、多行注释

2.变量

(1)nil

(2)number

(3)string

(3.1)字符串长度

(3.2)字符串拼接

(3.3)多行打印

(3.4)类型转换

(3.5)字符串操作(大小写、反转、查找、截取、重复)

(3.6)字符串与ASCII互转

(4)boolean

(5)function

(5.1)无参无返回值

(5.2)有参无返回值

(5.3)有一个返回值

(5.4)有多个返回值

(5.5)匿名函数

(5.6)可变形参函数

(5.7)获取函数类型

(5.8)函数嵌套

(5.9)函数嵌套-闭包

(6)table

(6.1)一维数组

 (6.2)unpack函数

(6.3)二维数组

 (6.4)自定义索引

(6.5)迭代器遍历

(6.5.1)ipairs

 (6.5.2)pairs

(6.6)字典

(6.7)类

(6.7.1)类函数声明

 (6.8)表操作

(6.8.1)插入

(6.8.2)删除

(6.8.3)排序

(6.8.4)拼接 

(7)thread

(7.1)协程创建

(7.2)协程运行

(7.3)协程挂起

(7.4)协程状态

 3.运算符

(1)算术运算符(+  -  *  /  %  ^)

(2)条件运算符(<  >  <=  >=  ==  ~=)

(3)逻辑运算符(and  or  not,支持短路规则)

4.条件分支语句

(1)单分支

(2)双分支

(3)多分支

5.循环语句

(1)for循环

(1.1)数值for循环

 (1.2)泛型for循环

(2)while...do...end循环

(3)repeat...until...循环

6.多脚本执行

(1)本地变量和全局变量

(2)require("脚本名")

(3)package.loaded["脚本名"]

(4)大G表

(5)dofile

7.特殊用法

(1)多变量赋值

(2)多返回值

(3)逻辑与、逻辑或

(4)实现三目运算符

8.元表

(1)设置元表

(2)特定操作

(2.1)__tostring

(2.2)__call

(2.3)运算符重载

(2.4)__index

(2.4.1)__index是表

(2.4.2)__index是方法

(2.5)__newindex

(2.5.1)__newindex是函数

(2.5.1)__newindex是表

(2.6)获取元表

9.面向对象

(1)封装

(2)继承

(3)多态

(4)总结

10.Lua其他库

(1)数学计算

(2)操作系统库-时间

(3)文件操作

(3.1)写文件

(3.2)读文件

(4)其他-路径

11.垃圾回收

三、热更新

1.插件安装

2.创建Lua脚本

3.unity中调用Lua脚本

4.项目升级

(4.1)Lua脚本更改

(4.2)unity中调用Lua 

(4.3)效果展示 


一、环境搭建

  • LuaForWindows:Releases · rjpcomputing/luaforwindows (github.com)
  • IDE:运用的是sublime_text

sublime_text快捷方式:

  • Ctrl+B:运行
  • Ctrl+L:选中当前行
  • Ctrl+/:快速注释
  • Ctrl+Shift+/:快速块注释

二、Lua语法

1.输出print、单行注释、多行注释

print("hello world")
--单行注释

--[[
多行注释
多行注释
]]

--[[
多行注释
多行注释
--]]

--[[
多行注释
多行注释
]]--

2.变量

  • lua中四种简单的变量类型:nil、string、boolean、number。除此之外还有:function、table、userdata、thread。
  • 不需要声明变量类型;可通过type函数获取变量类型,type函数返回值是string。
  • 变量可随便赋值。Lua 中的变量是动态类型的,这意味着变量没有预定义的类型,类型会在运行时根据赋给变量的值来决定。
  • lua中没有赋值过的变量,默认类型是nil。

(1)nil

a=nil
print(a)

(2)number

        所有的数值都是number类型。

a=10
print(a)

a=10.5
print(a)

(3)string

        在 Lua 中,字符串可以用双引号"、单引号'或者长括号[[ ]]来表示。

a="hello"
print(a)
(3.1)字符串长度

        使用#运算符可以获取字符串长度。

a="hello world"
print(#a)
a="一"    
print(#a)  --3
(3.2)字符串拼接

        可以使用..运算符来拼接字符串。也可以使用string.format来进行拼接。

a="一"    
print("一个汉字长度:"..#a)  --一个汉字长度是3
print(string.format("今年 %s 岁",'18'))
(3.3)多行打印

        在Lua中,可以使用多行字符串来打印多行文本。多行字符串可以通过在字符串的开始和结束使用双方括号[[]]来创建。也可以通过行末尾添加"\n"来进行多行打印。

a=[[1.hello
2.world]]
print(a)
(3.4)类型转换

        在Lua中,可以使用 tostring 函数将一个值转换为字符串。这个函数可以接受任何基本类型的值作为参数,并返回该值的字符串表示形式。

a=false
print(tostring(a))
(3.5)字符串操作(大小写、反转、查找、截取、重复)

        字符串相关操作,不会改变原字符串。Lua中索引从1开始。

str="YsL"
str1=string.upper(str)
print("原字符串:"..str..",全部大写为:"..str1)
str1=string.lower(str)
print("原字符串:"..str..",全部小写为:"..str1)
str1=string.reverse(str)
print("原字符串:"..str..",反转字符串为:"..str1)

--原字符串:YsL,全部大写为:YSL
--原字符串:YsL,全部小写为:ysl
--原字符串:YsL,反转字符串为:LsY
str="YsL"
print("---------字符串查找")
print(string.find(str,"sL")) --2  3

print("---------字符串截取")
print(string.sub(str,2))     --sL
print(string.sub(str,2,2))   --s

print("---------字符串重复:"..string.rep(str,3)) --字符串重复:YsLYsLYsL
print("---------字符串修改")
print(string.gsub("Session",'s','*'))    

--Se**ion	2

        string.gsub()返回两个结果,第二个值表示修改了几个字符。

(3.6)字符串与ASCII互转
a=string.byte("Lua",1)
print(a) --76

a=string.char(a)
print(a) --L

(4)boolean

a=false
print(a)
print(type(a)) --boolean
print(type(type(a))) --string

(5)function

        在Lua中,函数是一种可以封装代码以便重复使用的基本结构。函数可以接受参数,并且可以返回一个或多个值。Lua不支持重载,但重载后并不报错,默认调用最后声明的函数。

(5.1)无参无返回值
--先声明
function fun1()
	print("my fun1")
end

--再调用
fun1()
(5.2)有参无返回值
function fun3(a)
	print(a)
end

fun3(1)
fun3("hello world")
fun3(true)
fun3(nil) --nil
fun3()    --nil
fun3(1,2) --1     实参和形参个数不匹配,会补空或丢弃
(5.3)有一个返回值
function fun4(a)   --直接return即可
	return a
end

temp=fun4(1)
print(temp)
(5.4)有多个返回值
function fun5(a)   --支持返回多个值
	return a,-1
end

temp1,temp2,temp3=fun5(10) --支持多个变量来接取返回值
print(temp1)
print(temp2)
print(temp3)    --nil
(5.5)匿名函数
fun2=function()
	print("my fun2")
end

fun2()
(5.6)可变形参函数
function fun7(...)
	arg={...}        --arg表存储参数
	for i=1,#arg do
		print(arg[i])
	end
end
fun7(1,2,3,4,"hello",false)
(5.7)获取函数类型
fun6=function()
	print("my fun6")
end
print(type(fun6))   --function
(5.8)函数嵌套
function fun8()
	return function()
		print("函数嵌套")
	end
end

--调用方式1
fun9=fun8()
fun9()
--调用方式2
fun8()()
(5.9)函数嵌套-闭包

        在Lua中,函数可以嵌套在其他函数内部,这时候内部的函数可以访问外部函数的局部变量,这种结构称为闭包(closure)。闭包的强大之处在于,即使外部函数的执行已经结束,闭包仍然可以记住外部函数中的变量。

function fun10(x)
	return function(y)
		print(x+y)    --临时变量x的生命周期变长
	end
end

close=fun10(1)
close(2)

         即使 fun10执行完毕,close依然可以访问x这个变量。

(6)table

        表是 Lua 中唯一的数据结构,可以用来表示数组、字典等复杂数据结构。表在 Lua 中是非常强大的,因为它们可以包含所有类型的值。

(6.1)一维数组
a={1,2,3,4,nil,true,"hello",nil} --8个元素     如果数组的最后一个元素是 nil,Lua 通常会将其视为数组的结束,导致长度计算时忽略该 nil 元素。
print("数组a的长度:"..#a)  --7      lua5.1.5版本中,nil如果是数组最后一个元素,计算数组长度时则会忽略该元素。用#获计算长度不准确
print(a[0]) --nil  lua索引从1开始
print("第一个元素:"..a[1]) --1

print("---------------一维数组遍历")
for i=1,#a do
	print(a[i])
end
 (6.2)unpack函数

        在Lua中,unpack函数是一个非常有用的内置函数,它可以从表中取出一个连续的序列,并返回这些值。这个函数在Lua 5.1中是全局的,但从Lua 5.2开始,它被移到了table库中,即table.unpack

a = {1,2,3,4,nil,true,"hello",nil} 
print(unpack(a))  --1	2	3	4	nil	true	hello

        使用unpack可以很方便地将表中的值作为多个参数传递给函数。

        unpack或table.unpack接受三个参数:表、起始索引和结束索引。如果不指定起始索引和结束索引,默认情况下它会从第一个元素开始,到表的长度结束。

print(unpack(a,2,4))  --2	3	4
(6.3)二维数组
a={{1,2,3},{4,nil,"hello"}}
print(a[1][3]) --3
print(a[2][3]) --hello

print("---------------二维数组遍历")
for i=1,#a do
	b=a[i]
	for j=1,#b do
		print(b[j])
	end
end
 (6.4)自定义索引

        Lua的表不仅可以使用整数索引(不建议自定义整数索引),还可以使用字符串或其他Lua值(除了nil)作为键(后续会讲)。

a={[0]=1,2,3,[-1]=4,[4]=5}
print(#a)   --2   会去除索引不正常的元素
print(a[0]) --1

for i=1,#a do
	print(a[i])  --2 3
end

a={[1]=1,[2]=1,[4]=1,[6]=1}
print(#a)   --6
print(unpack(a)) --1	1	nil	1	nil	1
(6.5)迭代器遍历

         使用#计算长度并不准确,建议使用迭代器遍历:

  • 如果想要处理的是严格意义上的数组(索引连续的列表),使用ipairs更合适,因为它的性能可能会更好一些。
  • 如果需要处理的是一个混合了数组和字典风格的表,或者你不确定表的索引是否连续,那么使用pairs会更安全,它能确保遍历到表中的所有元素(nil除外)。
(6.5.1)ipairs

        从索引1开始遍历,索引小于0或者断序后无法遍历。

a={[0]=0,1,2,3,nil,4,5,[10]=6,c=7,nil}

for i,k in ipairs(a) do
	print("i:"..i.."-k:"..k)
end
-- i:1-k:1
-- i:2-k:2
-- i:3-k:3
 (6.5.2)pairs
a={[0]=0,1,2,3,nil,4,5,[10]=6,c=7,nil}

for i,k in pairs(a) do
	print("i:"..i.."-k:"..k)
end

--i:1-k:1
--i:2-k:2
--i:3-k:3
--i:5-k:4
--i:6-k:5
--i:0-k:0
--i:10-k:6
--i:c-k:7
(6.6)字典

         在Lua中,并没有专门的“字典”类型,但是表(table)结构在Lua中是非常灵活的,通常用来实现类似字典的功能。表可以存储键值对,其中键和值可以是任意类型的数据(除了nil)。当使用字符串作为键时,表就类似于其他语言中的字典或哈希表。

a={["name"]="Lisa",["age"]=22,["sex"]="girl",["1"]=true,c=56}

print("-------------访问")
print(a["name"])
print(a.c)
print(a.name)--虽然可以通过这种方法得到值,但键不能是数字,如a.1不对

print("-------------修改")
a["age"]=20
print(a["age"])

print("-------------新增")
a["Country"]="TaiLand"
print(a.Country)

print("-------------删除")
a["Country"]=nil
print(a.Country)

print("-------------字典的遍历,使用pairs")
for i,k in pairs(a) do
	print(i,k)
end
(6.7)类

        Lua中默认没有面向对象,所以不能new。

Student={
	age=12,
	name="Lisa",
	fun0=function(t) --在函数中需要调用表本身的属性和方法时,可以把自己当参数传进来
		print("fun0:"..t.name..":"..t.age) 
	end,
	fun1=function()
		--print("fun1:"..name..":"..age) --错误
		print("fun1:"..Student.name..":"..Student.age) --在函数中调用表本身的属性和方法,需要指明表名称
	end
}
--新增变量
Student.sex="girl"

print(Student.age)
print(Student.sex)

Student.fun0(Student)
Student:fun0() --语法糖:冒号调用方法时,会默认把调用者作为第一个参数传入方法中
Student.fun1()
(6.7.1)类函数声明

        和普通函数申明的两种方式(普通函数、匿名函数)相同,除此之外,也可以使用:来申明。

  • 当声明一个方法时,使用 : 语法允许定义一个需要隐式 self 参数的函数。self 实际上指的是调用方法的对象实例,而不是类名。
  • 当调用一个方法时,如果使用 : 语法,Lua会自动将调用该方法的表(对象)作为第一个参数传递给该方法。
Student.fun2=function()
    print("fun2")
end

function Student.fun3()
	print("fun3")
end

function Student:fun4() --语法糖:冒号声明方法时,会默认把调用者作为第一个参数传入方法中。self表示默认传入的第一个参数,self不是this。(self 实际上指的是调用方法的对象实例,而不是类名)
	print("fun4:"..self.name)
end

Student.fun2()
Student.fun3()

Student:fun4()
Student.fun4(Student)
 (6.8)表操作
(6.8.1)插入
t1={{name=1,age=10},{name=2,age=11}}
t2={name=3,age=12}

print(#t1)  --2
table.insert(t1,t2)

print(#t1)  --3
print(t1[3]["age"])  --12
(6.8.2)删除
table.remove(t1) --默认删除最后一个元素

table.remove(t1,1) --第二个参数,指定移除的元素索引
(6.8.3)排序

        在Lua中,table.sort 函数用于对表中的元素进行排序。这个函数可以对表中的元素进行原地(in-place)排序,也就是说,排序操作会直接改变原始表,而不是创建一个已排序的新表。

t3={12,53,6,2,4}
table.sort(t3)
print(unpack(t3)) --2	4	6	12	53

         默认情况下,table.sort 对表进行升序排序。如果想自定义排序顺序,可以提供一个排序函数作为table.sort的第二个参数。这个排序函数需要接受两个参数,并返回一个布尔值,表示第一个参数是否应该在排序后位于第二个参数之前。

table.sort(t3, function(a,b)
	if a>b then
		return true
	end
end)
print(unpack(t3)) --53	12	6	4	2

        上述代码表示:如果第一个参数大于第二个参数,就返回true,这样table.sort就会把较大的数放在前面。 

(6.8.4)拼接 

        在Lua中,table.concat函数用于将表中的元素连接成一个字符串。你可以指定一个连接符(也称为分隔符),用来在合并时插入元素之间。如果不指定连接符,默认使用空字符串(也就是没有分隔符)。

        table.concat还可以接受第二个和第三个可选参数,分别代表起始索引和结束索引,用于指定连接表中的哪一部分。

t4={1,"hello","world"}
str=table.concat( t4, "-", 1, 3 )
print(str) --1-hello-world

(7)thread

        Lua中的thread类型是指协同程序(coroutines),它们是由coroutine库提供的。在Lua中,术语"thread"通常指的是这些协同程序,而不是操作系统级别的线程。Lua的协同程序是协作式的,而非抢占式的,这意味着它们不会自行中断以让其他线程运行,而是显式地通过yield和resume来交换控制权。(Lua的线程可以让你编写出看起来像是同时执行的代码,但实际上它们是分时执行的,一个线程让出控制权后,另一个线程才会开始执行。)

        Unity 协程是与引擎的事件循环集成在一起的,允许在等待时继续处理其他事件和渲染更新。这与 Lua 中需要显式地调用 coroutine.resume 来恢复协程的执行有很大的不同。在 Unity 中,你只需要启动协程,引擎会在合适的时间点自动恢复它的执行。

        Lua中使用coroutine.create创建协程,每执行一次coroutine.resume,协程函数会前进至下一个coroutine.yield。

(7.1)协程创建

        协程的创建有两种方式:

  • coroutine.create():常用方式,返回线程类,协程的本质是线程对象
fun=function()
	print("hello 0223")
end

co=coroutine.create(fun)
print(type(co)) --thread 
  • coroutine.wrap():返回函数类型
fun=function()
	print("hello 0223")
end

co1=coroutine.wrap(fun)
print(type(co1))--function
(7.2)协程运行

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

  • coroutine.resume:对应create创建方式
  • 按函数方式运行
coroutine.resume(co)  --对应create创建方式

co1() --按函数方式运行
(7.3)协程挂起
fun2=function ()
	local i=1
	while true do
		print(i)
		i=i+1
		--挂起
		coroutine.yield(i) --返回i
	end
end

co2=coroutine.create(fun2)

temp1,temp2=coroutine.resume(co2) 
print(temp1,temp2) --true 	2     --true表示协程运行成功,temp2才是返回值i

temp1,temp2=coroutine.resume(co2) --运行一次执行一次
print(temp1,temp2) --true	3
(7.4)协程状态
  • coroutine.status(co)返回协同程序co的状态,可能的值有running、suspended、normal和dead。
  • coroutine.running() 得到正在运行的协程编号,要在函数内部打印。

        coroutine.wrap 返回的是一个函数,而不是协程对象,所以不能对它使用 coroutine.status。因为 coroutine.status 期望它的参数是一个协程,而不是一个函数。

fun=function()
	print("hello 0223")
	print(coroutine.running()) 
end
co=coroutine.create(fun)
coroutine.resume(co)
print(coroutine.status(co)) 

-- hello 0223
-- thread: 00D5E500
-- dead
fun=function()
	print("hello 0223")
	print(coroutine.running()) 
	coroutine.yield("hello")
end
co=coroutine.create(fun)
coroutine.resume(co)
print(coroutine.status(co)) 

-- hello 0223
-- thread: 00C9DD90
-- suspended

 3.运算符

        lua不支持位运算( & |)和三目运算符( ? :),也不支持++、--、 +=、 -=等算术运算符。

(1)算术运算符(+  -  *  /  %  ^)

print("加法:"..1+2)
print("123"+1)        --字符串进行算术运算,会自动转成number  
print("减法:"..1-2)
print("乘法:"..1*2)
print("除法:"..1/2)
print("余数:"..1%2)  
print("幂运算:"..3^2) 

--加法:3
--124
--减法:-1
--乘法:2
--除法:0.5
--余数:1
--幂运算:9

(2)条件运算符(<  >  <=  >=  ==  ~=)

print("1<2?"..tostring(1<2))
print("1>2?"..tostring(1>2))
print("1==2?"..tostring(1==2))
print("1~=2?"..tostring(1~=2))

--1<2?true
--1>2?false
--1==2?false
--1~=2?true

(3)逻辑运算符(and  or  not,支持短路规则)

print(true and false)
print(true or false)
print(not false)

--false
--true
--true

4.条件分支语句

        Lua 中的条件分支语句主要是通过 ifthenelseifelse 和 end 关键字来实现的。Lua中没有switch。

(1)单分支

a = 10
if a>5 then 
	print("a>5")
end

(2)双分支

a = 10
if a>5 then 
	print("a>5")
else 
	print("false")
end

(3)多分支

a = 10
if a>5 then 
	print("a>5")
elseif a==5 then   --注意:elseif要连着写
	print("a=5")
else 
	print("a<5")
end

5.循环语句

        在Lua中,循环语句允许重复执行一段代码多次。Lua提供了几种循环语句,包括for循环、while...do...end循环和repeat...until...循环。

(1)for循环

        Lua中的for循环有两种形式:数值for循环和泛型for循环。

(1.1)数值for循环
print("--------------for,默认递增1")
for i=1,5 do  
	print(i)
end

print("--------------for,自定义增量,递增2")
for i=1,5,2 do  
	print(i)
end

print("--------------for,自定义增量,递减1")
for i=5,1,-1 do  
	print(i)
end
 (1.2)泛型for循环

        用于迭代表(table)和其他迭代器。

for key, value in pairs(mytable) do
    print(key, value)
end

(2)while...do...end循环

a=5
while a<10 do
	print(a)
	a=a+1
end

(3)repeat...until...循环

        repeat...until...循环保证至少执行一次,即使条件从一开始就是假的。所以要防止死循环。

a=5
repeat 
	print(a)
	a=a+1
until a>10

6.多脚本执行

(1)本地变量和全局变量

        在Lua中,变量可以是全局的或局部的,这取决于它们是如何声明的。

  • 全局变量在Lua中不需要明确声明就可以使用。如果你赋值给一个未声明的变量,Lua会自动将其视为全局变量。全局变量在整个Lua程序中都是可见的,除非它们被特定的环境或模块限制。
  • 局部变量在Lua中使用关键字local进行声明。它们的作用域被限制在了它们被声明的代码块内。这意味着它们只能在定义它们的函数内部、控制结构内或代码块内被访问。
a=10         --全局变量
b="hello"    --全局变量
for i=1,5 do
	c=i*2    --c也是全局变量
end
print(c)     --10

for i=1,5 do
	local d=i*2  --局部变量
end
print(d)     --nil

(2)require("脚本名")

        在Lua中,require函数用于加载和运行库或模块。

  • 新建一个test.lua脚本(脚本可以return 一个局部变量)
print("this is test")
testA=10     --全局变量
local x = 1  --局部变量
x=x+1
return x
  • 在别的脚本中执行test.lua
require("test")        --注意是同一个路径
print(testA)           --可调用另一个脚本的全局变量
print(x)               --不可调用另一个脚本的局部变量

--this is test
--10
--nil

        当使用require来加载一个模块时,Lua会检查该模块是否已经加载过。如果已经加载过,Lua不会再次加载该模块,而是直接返回之前加载的结果。这是通过一个内部的包加载表(package.loaded)来实现的,其中存储了所有已加载模块的状态。如果在上述代码之后仍然require("test") ,则不会输出this is test。

(3)package.loaded["脚本名"]

        如果想检查一个模块是否已经被执行过(即已经被加载过),你可以检查 package.loaded 表中对应的条目。如果条目存在并且不是 nil,则表示该模块已经被加载过了。

print(package.loaded["test"])   --2
package.loaded["test"]=nil      --卸载
print(testA) --但是还会输出

        执行了print(package.loaded["test"])并且得到了输出 2,这是因为名为 "test" 的模块被加载,并且它返回了值:2。

temp=require("test") --可以加local,不加的话会存在_G中
print(temp)       --2
print(_G["temp"]) --2

        卸载后print(testA)仍然输出10,是因为:在Lua中,全局变量是存储在全局环境_G中的变量,这是一个存储所有全局变量的表。如果你加载并运行了一个Lua脚本,它定义了全局变量,即使之后你卸载了这个脚本,这些全局变量仍然会留在全局环境_G中,除非你显式地删除它们或者重置它们的值。

(4)大G表

        在 Lua 中,全局环境是由一个名为 _G 的特殊表来表示的。这个表被称为“全局表”(Global Table),简称“大G表”。它包含了所有的全局变量,你可以通过它访问或修改这些变量。Lua中的全局变量实际上是_G表的键值对。当你在Lua中创建一个全局变量时,实际上是在_G表中添加了一个元素。同样,访问一个全局变量就是在查询_G表。

for i,k in pairs(_G) do
	print(i,k)
end

        需要注意的是,直接操作 _G 表可能会影响到全局环境的完整性,所以通常建议谨慎使用,尤其是在大型项目或者多人合作的项目中。使用局部变量(local)可以避免不必要的全局污染。

(5)dofile

        在Lua编程语言中,dofile 和 require 都是用来加载和执行Lua文件的,但它们之间存在一些重要的区别。

  • dofile:这个函数会加载并执行指定的文件,但不会对其进行任何形式的缓存。也就是说,每次调用 dofile 时,Lua都会重新加载和执行该文件。如果你有一个文件需要多次执行,而且每次执行的结果都可能不同,那么 dofile 是一个不错的选择。
  • require:这个函数也会加载并执行指定的文件,但它会对文件进行缓存。当你第一次 require 一个文件时,Lua会加载和执行该文件,然后把文件的返回值存储在一个特殊的表中。当你再次 require 同一个文件时,Lua不会重新加载和执行该文件,而是直接返回这个表中的值。这意味着,如果你的文件只需要被执行一次,或者每次执行的结果都应该是相同的,那么 require 是一个更好的选择。
r=dofile("test.lua")   --注意需要加后缀
print(r)

--this is test
--2

7.特殊用法

(1)多变量赋值

        个数不对应,自动补nil或省略。

a,b,c,d,e=1,"hello",true,nil
print(a,b,c,d,e)

--1	hello	true	nil	nil

(2)多返回值

        个数不对应,自动补nil或省略。

function test()
	return 10,20,30
end
a,b=test()
print(a,b)

(3)逻辑与、逻辑或

        任何类型都能连接。只有nil和false才是假,0是真,"false"也是真。

print(1 and 2) --2   --1满足,判断2,返回2
print(0 and 1) --1   --0满足,判断1,返回1
print(1 or 2)  --1   --1满足,返回1(短路原则)
print(nil and 0) --nil
print(false or 1) --1
print(nil or "hello") --hello
print("false" or "1") --false
print("false" and "nil") --nil

(4)实现三目运算符

        lua不自持三目运算符。但是利用and和or可以实现三目运算符。

x,y=3,2
res=(x>y) and x or y
print(res) --3

x,y=3,4
res=(x>y) and x or y
print(res) --4

8.元表

        在Lua中,元表(Metatables)提供了一种方式来改变表的行为。每个表可以有一个元表,而这个元表中可以包含特殊的键,这些键对应的值是函数或者表,这些函数或表定义了原表在特定操作下的行为。

  • 任何表都可以作为另一个表的元表
  • 任何表都可以有自己的元表
  • 在子表中进行一些特定操作时,会执行元表中的内容

(1)设置元表

meta={}     --用于表示元表
mytable={}  --用于表示子表
--设置元表
setmetatable(mytable,meta) --参数一:子表,参数二:元表

(2)特定操作

(2.1)__tostring

        相当于重写字符串转换方法。当子表要被当作字符串使用时,会默认调用元表中的tostring方法。

meta2={
	__tostring=function() 
		return "hello meta2"
	end
} 
mytable2={} 
setmetatable(mytable2,meta2) 
print(mytable2) --hello meta2
meta3={
	__tostring=function(t) 
		return t.name
	end
} 
mytable3={
	name="Lisa"
} 
setmetatable(mytable3,meta3) 
print(mytable3) --Lisa
(2.2)__call

        当子表被当作函数来使用时,会默认调用这个元表中的__call。(默认不允许把普通的表当函数来调用,只有子表的元表声明了__call,子表才可以当函数调用)

        当你给一个表设置了元表,并且在这个元表中定义了 __call 函数后,你就可以直接调用这个表,就好像它是一个函数一样。Lua 将自动把表作为第一个参数传递给 __call 函数。

meta4={
	__tostring=function(t) 
		return t.name
	end,
	__call=function() 
		print("今天周五")
	end
} 
mytable4={
	name="Linda"
} 
setmetatable(mytable4,meta4) 
mytable4() --今天周五
meta5={
	__tostring=function(t) 
		return t.name
	end,
	__call=function(a,b)  --当希望传参数时,默认第一个参数是调用者自己,所以,print(a)时,会调用__tostring
	print(a,b)
end
} 
mytable5={
	name="Linda"
} 
setmetatable(mytable5,meta5) 
mytable5(1) --Linda  1
  • 当执行 mytable5(1) 时,Lua 会自动将mytable5作为第一个参数传递给__call 定义的函数,即a为mytable5。__call 被触发
  • 根据 __call 方法的定义,它会打印两个参数:a 和 b,即print(a)和print(b)
  • 由于 print(a) 被调用,这里的 a 是 mytable5,它会触发 __tostring 元方法,将 mytable5 转换为字符串,即它的 name 属性 "Linda"
  • 然后 print(b) 会打印传递的参数 1
(2.3)运算符重载

        在Lua中,元表(metatable)允许你改变表(table)的行为,其中之一就是通过元方法(metamethods)来重载运算符。运算符重载可以让你自定义两个表之间的运算行为,比如加法、减法、乘法等。(普通的表之间不支持加减乘除比较等)

  1. Lua在处理算术运算符的元方法(比如__add用于重载加法)时,只需要其中一个操作数有元表并且元表中实现了相应的元方法。Lua会首先检查左操作数(第一个操作数)的元表,如果没有找到对应的元方法,它会检查右操作数(第二个操作数)的元表。
  2. 比较运算符重载,需要确保两个子表的元表为同一个。
  • __add:定义了表相加的行为,(a + b)
  • __sub:定义了表相减的行为,(a - b)
  • __mul:定义了表相乘的行为,(a * b)
  • __div:定义了表相除的行为,(a / b)
  • __mod:定义了表进行模运算的行为,(a % b)
  • __pow:定义了表的乘幂运算,(a ^ b)
  • __unm:定义了表的取负运算,(-a)
  • __concat:定义了表的连接运算,(a .. b)
  • __len:定义了获取表长度的操作,(#a)
  • __eq:定义了表的相等比较,(a == b)
  • __lt:定义了表的小于比较,(a < b)
  • __le:定义了表的小于等于比较,(a <= b)
meta6={
	__add=function(t1,t2) --相当于运算符重载,当子表使用+运算符时,会调用该方法
	return t1.total+t2.total
end,
	__sub=function(t1,t2) --相当于运算符重载,当子表使用-运算符时,会调用该方法
	return t1.total-t2.total
end,
	__eq=function (t1,t2)
		if(t1.total==t2.total) then
			return true
		end
		return false
	end
} 
mytable6=
{
	total=10
} 
setmetatable(mytable6,meta6) 

mytable7=
{
	total=10
} 
print(mytable6+mytable7) --20
print(mytable6-mytable7) ---0
print(mytable6==mytable7)--false  --因为不是同一个元表

setmetatable(mytable7,meta6)      --设置为同一个元表
print(mytable6==mytable7) --true  
(2.4)__index

        当你尝试访问一个表中不存在的索引时,如果这个表的元表有__index元方法,Lua会调用这个方法。

        rawget:用于从表中获取一个键对应的值,但不触发任何元方法(metatable中的__index方法)。简单来说,它可以直接从表中获取值,而忽略任何可能已经设置的元方法。
 

        __index可以是一个函数或者一个表。

(2.4.1)__index是表

        如果__index是一个表,当你尝试访问第一个表中不存在的索引时,Lua会在__index指定的表中查找这个索引。

mytable8={}
meta8=
{
	age=10
}
setmetatable(mytable8,meta8)
print(mytable8.age)  --nil

meta8.__index=meta8  --元表补充__index,表指向自己,这种方法需要写到元表外
print(mytable8.age)  --10

meta8.__index={age=20}  --元表补充__index,这种写法可写在元表内,也可在元表外
print(mytable8.age)  --20

print(rawget(mytable8,"age")) --nil
mytable={}
meta=
{
	age=10
}
setmetatable(mytable,{__index=meta})
print(mytable.age)  --10
(2.4.2)__index是方法

        如果__index是一个函数,当你尝试访问表中不存在的索引时,Lua会调用这个函数。这个函数的第一个参数是表本身,第二个参数是被访问的索引。

        Lua 中如果 __index 元方法是一个函数且没有返回值,默认的行为是返回 nil。

mytable={}
meta=
{
	age=10
}
setmetatable(mytable,meta)
meta.__index= function()
	return 11
end
print(mytable.age)  --11
mytable={}
meta=
{
	age=10
}
setmetatable(mytable,meta)
meta.__index= function(table,value)
	value = 11
	return value
end
print(mytable.age) --11
(2.5)__newindex

        __newindex:当子表有元表,并且元表中有__newindex,你尝试对子表中不存在的属性赋值时:

  • 如果__newindex是一个函数,Lua会调用这个函数,传入子表、键和值作为参数。
  • 如果__newindex是一个表,Lua会在这个__newindex表中进行赋值操作,而不是在原始表中。

  __newindex可以用来控制对表的修改,实现只读表,或者在字段被修改时执行特定的行为。

(2.5.1)__newindex是函数
mytable11={}
print("子表地址"..tostring(mytable11))
meta11={
	__newindex=function(table,key,value) --参数table为子表
		print(table,key,value) 
	end
}
print("元表地址"..tostring(meta11))
setmetatable(mytable11,meta11)

mytable11.age=20         --对子表mytable11中不存在的属性age赋值

--子表地址table: 00A53478
--元表地址table: 00A531D0
--table: 00A53478	age	20
mytable11={}
print("子表地址"..tostring(mytable11))

meta11={
	__newindex=function(table,key,value) --参数table为子表
		print(table,key,value) 
	end
}
print("元表地址"..tostring(meta11))
setmetatable(mytable11,meta11)

mytable11.age=20         --对子表mytable11中不存在的属性age赋值
print(mytable11.age)     --nil
print(meta11.age)        --nil 

--子表地址table: 00F431F0
--元表地址table: 00F43240
--table: 00F431F0	age	20
--nil
--nil

         如果你对一个表的不存在的属性进行赋值,并且这个表有一个元表(metatable)且元表中重载了__newindex方法,那么Lua将调用这个__newindex方法而不是在原始表中创建这个属性。这意味着,如果__newindex方法没有显式地在表中创建这个属性,那么即使你尝试设置它,这个属性也不会存在于原始表中。

mytable11={}
print("子表地址"..tostring(mytable11))

meta11={
	__newindex=function(table,key,value) --参数table为子表
		print(table,key,value) 
		rawset(table,key,value)          --实际在子表中创建属性
	end
}
print("元表地址"..tostring(meta11))
setmetatable(mytable11,meta11)

mytable11.age=20         --对子表mytable11中不存在的属性age赋值
print(mytable11.age)     --20
print(meta11.age)        --nil 

--子表地址table: 00A83308
--元表地址table: 00A833D0
--table: 00A83308	age	20
--20
--nil
(2.5.1)__newindex是表
mytable10={}
meta10={
	__newindex={}
}
setmetatable(mytable10,meta10)

mytable10.age=20              --对子表mytable10 中不存在的属性age赋值
print(mytable10.age)          --nil
print(meta10.age)             --nil
print(meta10.__newindex.age)  --20
(2.6)获取元表
print(getmetatable(mytable))

9.面向对象

        Lua 本身不是一种面向对象编程语言,但它提供了机制,如元表(metatables)和元方法(metamethods),允许开发者模拟面向对象编程的特性,如封装和继承。

(1)封装

--定义一个Object类
Object={}
print("Object类:"..tostring(Object))
Object.id=10
Object.__index=Object

--Object类方法
function Object:Test()   --冒号:第一个参数是自己,运用self使用变量值
	print("Test function:"..self.id)    
end

--Object类构造函数
function Object:new()
	--运用元表
	local obj={} 	--创建子表
	setmetatable(obj,self)
	return obj  	--返回子表       
end

--创建Object子对象
temp=Object:new()    --或者temp=Object.new(Object)
print("子对象:"..tostring(temp))   
print(temp.id)   --10
temp:Test()      --Test function:10

--查看temp的元表
print("子对象的元表:"..tostring(getmetatable(temp)))
temp.id=2        --为temp创建了一个id属性,因为元表中没有__newindex。所以之前Object的id不变
print(Object.id) --10
temp:Test()      --Test function:2

--Object类:table: 00469648
--子对象:table: 00469698
--10
--Test function:10
--子对象的元表:table: 00469648
--10
--Test function:2

         如果Object.__newindex=Object,那么Object.id也会为2。但这通常不是面向对象编程中期望的行为,因为这样会导致所有 Object 类型的实例共享相同的 id 属性值,而不是每个实例拥有自己的 id 值。通常,你会希望每个实例都有自己的属性集,因此,通常会在 __newindex 方法中为实例表设置新的属性,而不是在其元表中设置。

(2)继承

-- 定义一个Object类
Object={}
print("Object类:"..tostring(Object))
Object.id=10
Object.__index=Object

--Object类构造函数
function Object:new()
	--运用元表
	local obj={} 	--创建子表
	setmetatable(obj,self)
	return obj  	--返回子表       
end

--创建Object类方法subClass,参数为子类
function Object:subClass(className)
	_G[className]={} 
	local obj=_G[className] --操作子类,不能写成local obj=className
	obj.base=self          --定义一个base属性,self为Object
	setmetatable(obj,self) 
	return obj
end

--创建Object子类Person
Object:subClass("Person")   --Person的元表是Object,Object设置了__index
print(Person.id)            --10

(3)多态

         多态性允许我们通过父类的引用来操作不同的子类对象,并且在运行时确定调用哪个类的重写方法。(相同的方法,执行不同的逻辑)
        父类= new 子类对象()

--创建一个Object的子类Father
Object:subClass("Father")
Father.__index=Father
Father.PosX=0;
Father.PosY=0
function Father:Move()
	self.PosX=self.PosX+1
	self.PosY=self.PosY+1
	print(self.PosX,self.PosY)
end
print("Father类:"..tostring(Father))

--创建一个Father的子类Son
Father:subClass("Son")
Son.__index=Son 
function Son:Move() --重写Move
	--想使用base.Move() ,
	print("self.base:"..tostring(self.base)) --self的base是Father
	self.base.Move(self)----要避免把基类表传入方法中,所以要用. --self是Son的实例
	--self.base:Move() --base用:会有问题,会共用posX posY   --self.base为father,默认参数也为father,那么就是father的Pos变化
end
print("Son类:"..tostring(Son))

--创建son的实例
local son1=Son:new()
son1:Move()

local son2=Son:new()
son2:Move()

--Father类:table: 00BD96C0
--Son类:table: 00BD9788
--self.base:table: 00BD96C0
--1	1
--self.base:table: 00BD96C0
--1	1

(4)总结

  • 封装:父类new函数中创建一个空表,并设置空表的元表为父类。需要运用__index。
  • 继承:父类实现一个方法,用于创建子类。函数参数为一个子表,并使用大G表来进行操作该子表。同样需要设置子表的元表为父类。需要运用__index。
  • 大坑:一定要注意self是指子类还是父类,可以打印一下地址查看! 

10.Lua其他库

        Lua 自带的标准库提供了一些基础的功能,除了上文提到的基础库(提供了Lua脚本与Lua解释器交互的基本函数,比如assert, error, ipairs, pairs, pcall, print, select, tonumber, tostring, type, unpack, xpcall等)、包管理(提供了Lua模块加载和构建的功能,主要函数包括require和module)、字符串处理、表处理(如table.insert,table.remove, table.sort等)、还有数学计算、输入/输出(提供了文件操作的函数)以及操作系统库(提供了与操作系统交互的函数)等库。

(1)数学计算

print("绝对值:"..math.abs(-11))
print("弧度转角度:"..math.deg(math.pi))
print("cos90度,参数是弧度:"..math.cos(90*(math.pi/180)))

print("向下取整:"..math.floor(0.56)) --0
print("向上取整:"..math.ceil(0.56))  --1

print("最大值:"..math.max(1,2))   --2
print("最小值:"..math.min(4,5))   --4

--将一个浮点数分解为其整数部分和小数部分
print(math.modf(1.2))   --1   0.2

print("幂运算:"..math.pow(2,5))
print("幂运算:"..2^5)   --不利用math库,使用 ^ 运算符通常更简洁

math.randomseed(os.time()) --必须初始化随机种子,否则随机数一定是固定的
print("随机数:"..math.random(0,100))
print("随机数:"..math.random(0,100))

print("开方:"..math.sqrt(4))

(2)操作系统库-时间

print(os.time())
print(os.time({year=2024,month=2,day=26}))
nowTime=os.date("*t")
print(nowTime.year.."/"..nowTime.month.."/"..nowTime.day)

--1711019493
--1708920000
--2024/3/21

(3)文件操作

(3.1)写文件
f=io.open("Lesson_20_IO.txt","w")
f.write(f,"流水从不争先,争的是滔滔不绝。\n")
f.write(f,"不盼刹那绽放,盼的是生生不息。")
io.close(f)
(3.2)读文件

        read函数是最常用的读取方法之一。read函数可以带有不同的参数来指定读取内容的方式:

  • "*l"或者"*line": [默认]读取下一行的内容,若为文件尾,则返回nil
  • "*n"或者"*number": 读取一个数字
  • "a"或者"*all": 从当前位置读取整个文件,若为文件尾,则返回空字串
  • number: 读取number个字符

        在Lua中,如果你想要将文件的读取光标(读取位置)移动到文件的第一行,你可以使用seek方法。seek方法用于设置和获取文件的当前位置,并且有三个选项:"set""cur""end"。如果你想要将光标移动到文件的开始,你应该使用"set"模式,并且将偏移量设置为0。

f=io.open("Lesson_20_IO.txt","r")
txt=""
while true do
	str=f.read(f)
	if str==nil then
		break
	end
	txt=txt..str
	print(txt)
end

f:seek("set")  --将光标移动到文件的开始,还支持参数:"cur", "end"
txt=f:read(6)
print(txt) --流水

f:seek("cur", 6)  -- 将光标向前移动10个字节
txt=f:read(6)
print(txt) --争先
txt=f:read("*number")
print(txt) --nil

io.close(f)


--流水从不争先,争的是滔滔不绝。
--流水从不争先,争的是滔滔不绝。不盼刹那绽放,盼的是生生不息。
--流水
--争先
--nil

(4)其他-路径

print("lua脚本加载路径:"..package.path)
path=package.path..";C:\\"
print("增加一个路径:"..path)

--lua脚本加载路径:;.\?.lua;D:\soft\Lua\5.1\lua\?.lua;D:\soft\Lua\5.1\lua\?\init.lua;D:\soft\Lua\5.1\?.lua;D:\soft\Lua\5.1\?\init.lua;D:\soft\Lua\5.1\lua\?.luac
--增加一个路径:;.\?.lua;D:\soft\Lua\5.1\lua\?.lua;D:\soft\Lua\5.1\lua\?\init.lua;D:\soft\Lua\5.1\?.lua;D:\soft\Lua\5.1\?\init.lua;D:\soft\Lua\5.1\lua\?.luac;C:\

11.垃圾回收

        lua中有自动定时进行GC操作。unity中热更新开发,尽量不要去用自动垃圾回收(推荐:切场景或者内存不够用时)

test={id=1024,index=0}
print("获取当前lua占用内存字节数(kb):"..collectgarbage("count"))

test=nil  --释放资源
collectgarbage("collect")  --进行垃圾回收
print("获取当前lua占用内存字节数(kb):"..collectgarbage("count"))

--获取当前lua占用内存字节数(kb):20.5048828125
--获取当前lua占用内存字节数(kb):19.392578125

三、热更新

1.插件安装

  • 在unity资源商店中找到moonsharp 插件,并导入到unity中。

2.创建Lua脚本

        创建Lua脚本test.lua,并放入StreamingAssets文件夹下。

function MyLua()
	print("hello lua")  --lua的print函数在Unity中是无法输出的
	Debug.Log("Hello unity,I am Lua.")
	return "hello lua"
end

3.unity中调用Lua脚本

        下方实例是用来展示:实现Lua调用unity中Debug类,并在unity中成功调用Lua中的方法。

using MoonSharp.Interpreter;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class LuaManager : MonoBehaviour
{
    void Start()
    {
        //创建Lua脚本解释器
        Script luaScript = new Script();

        //在Lua中调用Unity提供的Debug功能
        UserData.RegisterType<Debug>();
        luaScript.Globals["Debug"] = typeof(Debug);

        //获取lua脚本位置
        string luaPath = System.IO.Path.Combine(Application.streamingAssetsPath, "test.lua");

        //读取lua脚本
        string luaText = System.IO.File.ReadAllText(luaPath);

        //运行lua脚本
        DynValue dynValue=luaScript.DoString(luaText);
        if(dynValue == null )
        {
            Debug.Log("test.lua不存在");
            return;
        }
        //获取lua脚本中的函数
        DynValue luaFunction = luaScript.Globals.Get("MyLua");
        if(luaFunction == null )
        {
            Debug.Log("MyLua 函数不存在");
            return;
        }
        DynValue result = luaScript.Call(luaFunction);
        Debug.Log("unity中运行结果:"+result.ToString());
        Debug.Log("unity中运行结果:" + result.ToObject<string>());
    }
}

4.项目升级

        Lua利用unity中的GameObject类、Vector3类创建一个物体并返回。

(4.1)Lua脚本更改

function MyLua()
	go=GameObject("LuaObj")
	go.transform.position=Vector3(1,1,1)
	print("hello lua")  --lua的print函数在Unity中是无法输出的
	Debug.Log("Hello unity,I am Lua.")
	return go
end

(4.2)unity中调用Lua 

    private void Test()
    {
        //获取lua脚本位置,lua脚本可以放在服务器端,通过www下载,下载后再运行Lua脚本
        string luaPath = System.IO.Path.Combine(Application.streamingAssetsPath, "test.lua");

        //读取lua脚本
        string luaText = System.IO.File.ReadAllText(luaPath);

        if(luaText=="")
        {
            return;
        }
        //创建Lua脚本解释器
        Script luaScript = new Script();

        //在Lua中调用Unity提供的功能
        UserData.RegisterType<Debug>();
        UserData.RegisterType<GameObject>();
        UserData.RegisterType<Transform>();
        UserData.RegisterType<Vector3>();
        //将Debug类的类型信息赋值给Lua中的全局变量Debug。这样Lua脚本就可以通过Debug这个全局变量来访问Debug类的静态方法和属性。
        luaScript.Globals["Debug"] = typeof(Debug);

        //Debug是一个静态类,其方法可以直接在Lua中调用,而GameObject和Vector3是需要实例化的对象,它们在Lua中的使用需要C#层面上的支持和注册。
        //创建一个函数委托,这个委托指向一个方法CreateGameObject,这个方法接受一个string参数并返回一个GameObject实例。这样Lua脚本就可以通过调用GameObject("name")来创建一个新的GameObject实例。
        //在Lua中创建Unity对象通常需要调用Unity的API函数,而不仅仅是访问类型信息。对于GameObject,你不能直接使用typeof(GameObject),因为GameObject不仅有静态方法,还有实例方法和属性。如果你只是传递类型信息,Lua将不知道如何创建GameObject的实例。
        luaScript.Globals["GameObject"] = (System.Func<string, GameObject>)CreateGameObject;

        luaScript.Globals["Vector3"] = (System.Func<float, float, float, Vector3>)CreateVector3;


        //运行lua脚本
        DynValue dynValue = luaScript.DoString(luaText);
        if (dynValue == null)
        {
            Debug.Log("test.lua不存在");
            return;
        }
        DynValue luaFunction = luaScript.Globals.Get("MyLua");
        if (luaFunction == null)
        {
            Debug.Log("MyLua 函数不存在");
            return;
        }
        DynValue result = luaScript.Call(luaFunction);

        //返回结果是字符串
        //Debug.Log("unity中运行结果:" + result.ToString());
        //Debug.Log("unity中运行结果:" + result.ToObject<string>());

        //返回结果是物体
        GameObject go=result.ToObject<GameObject>();
        go.AddComponent<Rigidbody>();
    }
    static GameObject CreateGameObject(string name)
    {
        return new GameObject(name);
    }

    static Vector3 CreateVector3(float x, float y, float z)
    {
        return new Vector3(x, y, z);
    }

(4.3)效果展示 

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

LeetCode每日一题——数组串联

数组串联OJ链接&#xff1a;1929. 数组串联 - 力扣&#xff08;LeetCode&#xff09; 题目&#xff1a; 思路&#xff1a; 题目说 ans 由两个 nums 数组 串联 形成。那么我们就只需要历遍两次nums数组&#xff0c;将它放在我们的ans数组里。 注意&#xff1a; 题目函数对于我…

Day22:过滤敏感词、开发发布帖子、帖子详情

过滤敏感词 前缀树 - 名称:Trie、字典树、查找树 - 特点:查找效率高&#xff0c;消耗内存大 - 应用:字符串检索、词频统计、字符串排序等在这里插入图片描述 敏感词过滤器的步骤 根节点不包含任何字符&#xff1b;其余每个节点只有一个字符&#xff1b;连接起来一条路就是字…

StarRocks 助力金融营销数字化进化之路

作者&#xff1a;平安银行 数据资产中心数据及 AI 平台团队负责人 廖晓格 平安银行五位一体&#xff0c;做零售金融的领先银行&#xff0c;五位一体是由开放银行、AI 银行、远程银行、线下银行、综合化银行协同构建的数据化、智能化的零售客户经营模式&#xff0c;这套模式以数…

python 爬虫爬取地理空间高程图GDEMV2 30m 中国地形

一.配置Python 爬虫 环境 from selenium import webdriver import time # from selenium.webdriver.common.action_chains import ActionChains from selenium.webdriver.common.by import Byfrom selenium.webdriver.common.keys import Keys # from selenium.webdriver.comm…

算法体系-14 第十四 贪心算法(上)

一 、 递归套路解决判断完全二叉树 1.1 描述 1.2 分析 1.3 代码 public static boolean isCBT2(Node head) {return process(head).isCBT;}public static class Info {public boolean isFull;public boolean isCBT;public int height;public Info(boolean full, boolean cbt…

学习人工智能:Attention Is All You Need-1-介绍;Transformer模型架构

Transformer模型是目前最成功的chatGPT&#xff0c;Sora&#xff0c;文心一言&#xff0c;LLama&#xff0c;Grok的基础模型。 《Attention Is All You Need》是一篇由Google DeepMind团队在2017年发表的论文&#xff0c;该论文提出了一种新的神经网络模型&#xff0c;即Trans…

个人可以做知识付费网站吗

个人可以做知识付费网站吗 个人能够做学问付费网站吗&#xff1f;答案是肯定的&#xff01;如今个人做学问付费网站并不需求太多的资金和技术支持&#xff0c;我们只需求购置一台效劳器或虚拟主机&#xff0c;然后在该主机空间上搭建一个WordPress网站&#xff0c;最后运用带有…

0基础学习VR全景平台篇第146篇:为什么需要3D元宇宙编辑器?

一.什么是3D元宇宙编辑器&#xff1f; 3D元宇宙编辑器是全新3DVR交互渲染创作工具&#xff0c;集3D建模、虚拟展厅、AI数字人等能力&#xff0c;渲染和虚拟现实技术于一身的生产力工具。 具有跨平台和随时随地编辑等特点&#xff0c;可广泛应用于展会、展厅、博物馆、可视化园…

基于python+vue的幼儿园管理系统flask-django-php-nodejs

随着信息时代的来临&#xff0c;过去的传统管理方式缺点逐渐暴露&#xff0c;对过去的传统管理方式的缺点进行分析&#xff0c;采取计算机方式构建幼儿园管理系统。本文通过课题背景、课题目的及意义相关技术&#xff0c;提出了一种活动信息、课程信息、菜谱信息、通知公告、家…

MySQL索引(图文并茂)

目录 一、索引的概念 二、索引的作用 三、创建索引的原则依据 四、索引的分类和创建 1、索引的分类 2、索引的创建 2.1 普通索引 2.1.1 直接创建索引 2.1.2 修改表方式创建 2.1.3 创建表的时候指定索引 2.2 唯一索引 2.2.1 直接创建唯一索引 2.2.2 修改表方式创建 …

机器学习——决策树(三)预剪枝

观前提示&#xff1a;这是本人机器学习决策树内容的第三篇博客&#xff0c;沿用了之前相关的代码&#xff0c;包括信息增益计算函数、结点类、预测函数和分类精度计算函数 完整代码指路 DrawPixel/decisionTree.ipynb at main ndsoi/DrawPixel (github.com) 前两篇博客详见…

水泥领域智慧工厂物联网解决方案

水泥领域智慧工厂物联网解决方案 在水泥生产行业中&#xff0c;构建智慧工厂物联网解决方案已经成为推动产业升级、实现智能制造的关键路径。该方案深度融合了先进的信息技术与传统的水泥生产工艺&#xff0c;通过全面感知、可靠传输、智能处理等环节&#xff0c;实现了对整个…

Macos docker安装达梦数据库

官网下载达梦docker镜像安装包 导入安装包 docker load -i /Users/yeungsinsin/Downloads/dm8_20230808_rev197096_x86_rh6_64_single.tar查看导入的镜像 docker images4. docker run 启动容器 docker run -d -p 30236:5236 --restartalways --name dm8 --privilegedtrue -e…

是德科技keysight N9917A微波分析仪

181/2461/8938产品概述&#xff1a; N9917A 是一款使用电池供电的便携式分析仪&#xff1b;基本功能是电缆和天线分析&#xff1b;配置还包括频谱和网络分析仪、可选的内置功率计和矢量电压表。 N9917A FieldFox 手持式微波分析仪 主要特性和功能 18 GHz 最大频率&#xfef…

基于Springboot的闲置图书分享(有报告)。Javaee项目,springboot项目。

演示视频&#xff1a; 基于Springboot的闲置图书分享&#xff08;有报告&#xff09;。Javaee项目&#xff0c;springboot项目。 项目介绍&#xff1a; 采用M&#xff08;model&#xff09;V&#xff08;view&#xff09;C&#xff08;controller&#xff09;三层体系结构&…

MySQL的基础操作与管理

一.MySQL数据库基本操作知识&#xff1a; 1.SQL语句&#xff1a; 关系型数据库&#xff0c;都是使用SQL语句来管理数据库中的数据。 SQL&#xff0c;即结构化查询语言(Structured Query Language) 。 SQL语句用于维护管理数据库&#xff0c;包括数据查询、数据更新、访问控…

Sora底层技术原理:Stable Diffusion运行原理

AIGC 热潮正猛烈地席卷开来&#xff0c;可以说 Stable Diffusion 开源发布把 AI 图像生成提高了全新高度&#xff0c;特别是 ControlNet 和 T2I-Adapter 控制模块的提出进一步提高生成可控性&#xff0c;也在逐渐改变一部分行业的生产模式。惊艳其出色表现&#xff0c;也不禁好…

[BT]BUUCTF刷题第3天(3.21)

第3天&#xff08;共4题&#xff09; Web [极客大挑战 2019]LoveSQL Payload&#xff1a;/check.php?username1 union select 1,(select group_concat(password) from l0ve1ysq1),3%23&password1 看到登录页面&#xff0c;先尝试万能密码&#xff1a; 用户名&#xff…

Hive和Hadoop版本对应关系

通过 Downloads (apache.org) 即可查看

Java算法总结之插入排序(详解)

程序代码园发文地址&#xff1a;Java算法总结之插入排序&#xff08;详解&#xff09;-程序代码园小说,Java,HTML,Java小工具,程序代码园,http://www.byqws.com/ ,Java算法总结之插入排序&#xff08;详解&#xff09;http://www.byqws.com/blog/3148.html?sourcecsdn 插入排…