一.变量
在lua中所有的变量申明,都不需要申明变量类型,会根据数值自动判断类型
1. 简单变量类型
(1) nil 类似于C#中的null,nil即表示空也是一种类型
可以使用不申明的变量,打印出来就为nil;使用type(a)可以判断a的变量类型
a = nil
print(a)
print(b) --可以使用不申明的变量,打印出来就为nil
print(type(a))
print(type(type(a))) -- 判断type的变量类型
(2) number lua中所有的数值都属于number
无论是小数或者整数,只要是数值在lua中都被认为是number类型
num1=1
num2=5.9
print(num1)
print(num2)
print(type(num1))
(3) string 其它语言中的char和string都属于string类型
str1 = "123"
str2 = '你'
print(str1)
print(str2)
print(type(str1))
(4) boolean 和其他语言的布尔值一样
bool = false
print(bool)
print(type(bool))
(5) 多变量赋值 数值不匹配则根据多删少补空来处理
local a,b,c = 1,true,"21252"
print(a)
print(b)
print(c)
2. 字符串
在lua中也包含许多关于字符串的方法,方便字符串的使用
- **( #string } 计算字符串长度 (其中每个汉字占3个长度) **
str = "aBCdeFG你好1"
print(#str) -- 14
- { [[]] }字符串多行打印 lua中同样可以支持转义字符\n
print("123\n456")
s=[[
你好
我出门了
你呢
]]
print(s)
- { .. } 字符串拼接 也可以使用C语言中的format
print("123" .. "456")
print(000 .. 111)
-- %d:数字占位符 %a:任何字符占位符 %s:字符占位符
print(string.format("我%d岁",10))
- 其他类型转化为字符串tostring()
data = 555
print(tostring(data))
- 字符串的公共方法 (和其他语言基本一样)
str = "aaaBBBcccDD"
print(string.upper(str)) -- 转化大写
print(string.lower(str)) -- 转化小写
print(string.reverse(str)) --字符串倒置
print(string.find(str,"Bcc")) --子字符串查找。(6 8) 会打印出子符串的开始和结尾索引
print(string.sub(str,3)) --字符串截取,截取当前索引以及之后的字符串
print(string.sub(str,5)) --字符串截取,截取索引范围内的字符串
print(string.rep(str,2)) --字符串重复,根据后面参数重复当前字符串
print(string.gsub(str,"B","*")) --修改字符串,会打印修改后的字符串和修改次数
a = string.byte("Lua",1) --字符转ASCII码,参数指定字符的索引
print(a)
print(string.char(a)) --ASCII码转字符
3. 运算符
在lua语言中的运算符存在与其他语言不同的地方:
- 没有自增自减和复合运算符
- 不支持位运算符
- 不支持三目运算符
- 算数运算符
在字符串中的数值计算会自动转换为number类型再打印出来
print("加法运算".. 1 + 2)
print("123.4" + 21)
print("123.4" - 1)
print( "取余运算".."123" % 2)
print( "幂运算".."2" ^ 2)
- 条件运算符 > < == <= >= ~=不等于
print(3 >= 1)
print(3 ~= 1) --不等于
- 逻辑运算符 and与 or或 非not 支持短路判断
print(true and false)
print(true or false)
print(not false)
- and和or的特殊使用
不仅可以使用布尔值来连接判断。在lua中只有nil和false才为假
--根据短路原则,and需要判定到后一个值才能确实布尔值,输出为最后判断的值
print(2 and 1) -- 判断2为true继续判断,所以输出为1
print(2 or 1)
local x,y = 10,25
local result = (x>y) and x or y;
print(result) -- 25
4. 复杂数据类型
(1) 函数 function
(2) 表 table
(3) 数据结构 userdata
(4) 协同程序 thread(线程)
二.选择_循环语句
在lua中同样存在一些选择循环语句,但格式往往有些不同。并且在lua中并不存在switch循环语句。
1. 条件分支语句
- 单分支
使用if...then...的形式来进行判断分支语句
a= 5
b= 10
-- 支持and,or
if a >5 and b<20 then
print(a)
end
if a >5 or b<20 then
print(a)
end
- 多分支
使用 if ... then ... elseif ... then ... else ... end的形式来进行多判断分支语句
if a >5 and b<20 then
print(a)
elseif a>6 then -- 多分支的elseif中间不需要空格
print(b)
else
print("以上都不满足")
end
2. 循环
- while循环
使用 while ... do ... end 的形式来进行循环语句
num =1;
while num<5 do
print("当前num等于".. num)
num = num +1
end
- do..while循环
使用 repeat ... until ... 的形式来进行循环语句
repeat --进入循环 类似do
print("当前x等于".. x)
x = x -1
until x < 2 --跳出循环的条件 类似while(x<2)
- for循环
使用 for ... do ... end 的形式来进行循环语句,不指定的话默认递增
for i =1,5 do
print("i="..i)
end
-- 第三个参数表示步长,默认为1,负数表示递减
for a =10,5,-1 do
print("a="..a)
end
- 迭代器遍历
(1) ipairs迭代器
类似#arr遍历,只能找到连续索引的键值,不连续索引无法遍历 ,需要给定两个遍历来获取索引和对应的值
a={[0] =1,2,3,[4] = 10,[-1] =20}
-- ipairs迭代器
for i,k in ipairs(a) do
print("索引:"..i..",值:"..k)
end
(1) pairs迭代器 无论任何索引都可以遍历出来,需要给定两个遍历来获取索引和对应的值
a={[0] =1,2,3,[4] = 10,[-1] =20}
-- ipairs迭代器
for i,k in pairs(a) do
print("索引:"..i..",值:"..k)
end
三.函数
函数方法必须写在调用前面,否则调用找不到函数方法
1. 函数定义
function 方法名()...end 或者 方法名=function()...end 中定义方法名和方法体。
-- 第一种定义函数
function newMethod()
print("newMethod函数方法")
end
-- 第二种定义函数
method = function()
print("带函数的变量method")
end
2. 无参无返回值的函数
function func()
print("func函数方法")
end
func1 =function()
print("func1函数方法")
end
--调用方法
func()
func1()
3. 有参无返回的函数
function 方法名(参数)...end 和其他语言一样,参数写在函数括号中
function funcPara(str)
print(str)
end
funcPara(10)
funcPara() --调用有参函数不给参数默认给空nil
funcPara(2,3) -- 参数不符合但只会匹配对应参数,多余的舍弃
4. 有返回值有参的函数
- 直接在方法体内添加return
- return可以同时返回出多个变量,但必须使用多个变量来获取,否则直接抛弃
function funcReturn(str,a)
return str,a
end
function funcReturn1(str)
return str,true
end
-- 多返回值可以通过多个变量来承接多个返回值
t1,t2 = funcReturn1(5)
print(t1)
print(t2)
5. 函数类型
函数类型都是function
funcType = function()
print(1111)
end
print(type(funcType)) --函数类型都是function
6. 函数重载
lua不支持函数重载,如果强制调用重载函数,他会默认调用最后一个重载函数
7. 变长参数
参数中为三个点,则参数是可变的,会使用一个表存起来,在使用。调用时候自己添加自定义个参数
function trans(...)
arr = {...}
for i = 1,#arr do
print(arr[i])
end
end
trans("111",22,true,12.2,'ddd')
8. 函数嵌套
在一个方法内定义另一个或多个方法
function out()
--直接返回函数方法
return function()
print("嵌套函数in")
end
end
-- 函数也算是一种变量,所以也可以使用参数来获取和传递
test = out()
test()
- 函数嵌套中的闭包问题
闭包是一个函数和其相关引用环境的组合体。 闭包可以将函数内部的变量保持在内存中,并在函数内部和外部之间共享这些变量。
function bao1(num1)
--此时num1参数生命周期发生变化,会继续将num1给到返回函数中
return function(num2)
return num1 + num2
end
end
bao2 = bao1(2)
print(bao2(3)) --5
四. 表table
在lua语言中,其实所有的复杂类型统称为表
1.Array数组
1. 数组array
在lua中的数组是可以有多种类型的,不指定具体类型,并且索引都是从1开始
-- 数组并不指定一个类型,可以任意类型
arr = {1,20,5,nil,'a',"abc",true}
print(arr[1])
(1). 数组的遍历
- 可以使用for循环,#arr表示数组长度,数组中存在nil同样会正常打印出来
- lua5之前的版本,当数组中存在nil会忽略以及之后的元素
arr = {1,20,5,nil,'a',"abc",true}
for i=1,#arr do
print(arr[i])
end
2.二维数组
arr = {{1,2,3},{'1','2','3'},{true,false,false}}
print(arr[2][3])
end
(1).二维数组的遍历
双for循环或使用迭代器遍历
arr = {{1,2,3},{'1','2','3'},{true,false,false}}
for i=1,#arr do
b = arr[i]
for j=1,#b do
print(b[j])
end
end
3.自定义索引
可以指定某个值的索引,可以是负数索引,没有指定的值依旧按照索引为1开始
newarray = {[0] = 10,[10]=20,30,[-10]=40,50}
print(newarray[1])
--修改了索引的数值不计算到表的长度
print(#newarray) -- 结果为2
2.字典
- 字典
在lua中的字典和数组基本类似,对于一个键值对表示:[键]=值
a = {["1001"]=10,["1002"]=true,["name"]=30,["1004"]='A',["1005"]=50}
print(a["1001"])
--可以通过字典.键的形式获取对应的值,但是键中不能是纯数字 print(a.1001)
print(a.name)
-- 修改键值对
a.name = "更改"
print(a.name)
-- 修改键值对
a.sex = "男"
print(a.sex)
- 遍历字典
a = {["1001"]=10,["1002"]=true,["name"]=30,["1004"]='A',["1005"]=50}
for k,v in pairs(a) do
print(k,v)
end
--只获得值,使用_下划线不给定变量输出只打印值参数
for _,v in pairs(a) do
print(v)
end
3.类与结构体
lua中没有面向对象的实现,类无法通过new去创建对象
- 类与结构体定义和使用 可以定义字段和方法,每个变量后都需要用逗号分开
Student = {
name = "卷卷",
age,
Func = function()
print(Student.age) --当函数内需要访问类中的变量,必须指定类名.变量
print("牛逼")
end,
Learn = function(stu)
print(stu.name)
print("学习方法")
end,
}
--调用类中的变量或函数
Student.age = 5
print(Student.age)
Student.Func()
-- 使用冒号调用方法会自动将调用者作为方法的参数
-- 而使用点调用则必须要填入参数否则报错
Student:Learn()
-- 只能在外部声明方法的时候可以使用冒号,在内部不可以使用冒号调用
function Student:Talk()
print(self.name.."正在说话") --self表示默认传入的第一个参数
end
Student:Talk()
4.表的公共操作
t1 = {{name = "卷卷",age = 18},{name = "哈哈",age = 21}}
t2 = {name = "嘿嘿",age = 15}
print(#t1) -- 表内数据量
--插入方法
table.insert(t1,t2)
print(t1[3].age)
--移除方法,默认是移除最后一个元素表,后面参数表示要移除的索引值对应的内容
table.remove(t1,1)
print(t1[1].name)
--排序,对表内数据排序,默认为升序排序
t2 = {10,50,30,580,45,32,80,674}
table.sort(t2)
for i=1,#t2 do
print(t2[i])
end
--降序排列 第二个参数为排序规则函数
table.sort(t2, function(a,b)
if a>b then
return true
end
end)
for i=1,#t2 do
print(t2[i])
end
-- 字符串拼接
strtable = {"你好","在忙","睡了"}
str = table.concat(strtable,"--") --连接函数,用来拼接表中元素,返回值为一个字符串
print(str)
五. 多脚本执行
在多个lua脚本之间进行数据获取和交互
1.全局变量和本地变量
--全局变量
a = 1
b = true
--本地变量
local str = "本地变量"
for i =1,2 do
local num = "循环"
print(num)
end
print(num) -- 无法拿到本地变量,输出为nil
2.多脚本执行
通过内置的包路径去查询lua脚本文件,再进行访问数据
- user.lua脚本
-- 可以添加相对位置的包路径到内置包路径中
package.path = "luaRun/?.lua;"..package.path
--根据package.path的路径寻找这个名字的脚本,脚本只加载一次
require("Test")
-- 访问Text.lua中的变量
print(squre1)
print(currentlocal)
-- 要获取外部脚本的本地变量,要使用本地变量来获取,并且外部脚本已经return出本地变量
local testlocal = require("Test") --拿到返回出来的本地变量
print(testlocal)
- Test.lua脚本
print("加载Test脚本")
-- 在该脚本中放置全局变量和本地变量
squre1 = "全局"
local currentlocal = "本地"
return currentlocal --可以将本地变量返回出去给其他脚本使用
3.多脚本执行的脚本卸载
通过内置的包路径去查询lua脚本文件,再进行访问数据
-- 获取当前脚本是否被加载过
print(package.loaded["Test"]) --返回布尔值,是否加载。如果脚本中有返回值则打印返回值
--卸载加载的脚本
package.loaded["Test"] = nil
print(package.loaded["Test"]) --打印为nil,而不是false
4.大G表
_G表是一个总表,他将所有申明出来的全局变量存储到这个大G表中
--遍历所有大G表中的全局变量,而本地变量并不存在于大G表中
for k,v in pairs(_G) do
print(k,v)
end
六. 协同程序(协程)
在多个lua脚本之间进行数据获取和交互
1.协程的创建
--使用 coroutine.create() 创建线程类型的协程
local create1 = function()
print("coroutine.create()创建协程")
end
coc = coroutine.create(create1)
print(coc) --会打印出来协程的地址,并且类型属于线程thread对象
--使用 coroutine.wrap()创建函数类型的协程
local wrap1 = function()
print("coroutine.wrap()创建协程")
end
cop = coroutine.wrap(wrap1)
print(cop) --会打印出来协程的地址,但是类型属于函数类型
1.协程的创建
- 使用 coroutine.create() 创建线程类型的协程类型属于thread对象
- 使用 coroutine.wrap() 创建函数类型的协程类型属于函数类型
--使用 coroutine.create() 创建线程类型的协程
local create1 = function()
print("coroutine.create()创建协程")
end
coc = coroutine.create(create1)
print(coc) --会打印出来协程的地址,并且类型属于线程thread对象
--使用 coroutine.wrap()创建函数类型的协程
local wrap1 = function()
print("coroutine.wrap()创建协程")
end
cop = coroutine.wrap(wrap1)
print(cop) --会打印出来协程的地址,但是类型属于函数类型
2.协程方法的调用
- coroutine.create()创建的协程使用coroutine.resume()调用协程
- coroutine.wrap()创建的协程可以当作函数直接调用即可
--匹配coroutine.create()创建的协程使用coroutine.resume()
coroutine.resume(coc)
--匹配coroutine.wrap()创建的协程可以当作函数直接调用即可
cop()
3.协程的挂起
- 使用coroutine.yield()挂起协程
-- 使用coroutine.yield()挂起协程
local create2 = function()
local i = 1
while true do
print("循环"..i)
i= i+1
coroutine.yield(i)
end
end
cocs1 = coroutine.create(create2)
coroutine.resume(cocs1)
coroutine.resume(cocs1) --执行几次就会循环几次
temp1,temp2 = coroutine.resume(cocs1)
print(temp1,temp2) --默认会有返回值,第一个为是否成功执行,第二个为当前协程挂起中的值
cop()
cocs2 = coroutine.wrap(create2)
cocs2() --create和wrap分别执行属于不同的线程上
4.协程的状态
- dead协程已经停止,suspended协程被暂时挂起,running 协程正在运行
- coroutine.status(协程对象) 查看一个协程处于的状态
- coroutine.running() 如果在外部调用,协程已经结束或者暂停了所以总是nil
print(coroutine.status(cocs1))
coroutine.running() 获取当前正在运行的协程的编号
七. 元表
- 所有的表的变量都可以作为另一个表变量的元表
- 任何表变量都可以有自己的元表
- 拥有元表的表变量在进行一些操作的时候都会执行元表的内容
1.元表的设置
- setmatatable(子表,元表)给子表设置一个元表,仅仅是设置,无其他操作
table = {}
mytable = {}
setmetatable(mytable,table)
2.元表的操作
(1) __tostring方法
当子表输出字符串的时候会默认调用__tostring方法,并且可以自动传入参数
table1 = {
__tostring = function()
return "元表的__tostring"
end
}
mytable1 = {}
setmetatable(mytable1,table1)
print(mytable1) -- 元表的__tostring
(2) __call方法
当子表当作函数使用的时候会默认调用__call方法,并且可以自动传入参数
table2 = {
__tostring = function()
return "元表的__tostring"
end,
__call = function(x,y)
--函数中的两个参数,第一个默认为调用者自身,第二个才是调用传进来的参数
print(x)
print(y)
print("元表的__call")
end
}
mytable2 = {}
setmetatable(mytable2,table2)
mytable2(1)
(3) 运算符重载方法
根据特定的函数名称可以对运算符进行重载,根据自己的运算规则给运算符指定新的规则
table3 = {
--add重载了+运算符,将相加的逻辑写在方法里,然后可以直接使用+
__add = function(t1,t2)
return t1.age + t2.age
end,
--sub重载了-运算符,将相减的逻辑写在方法里,然后可以直接使用+
__sub = function(t1,t2)
return t1.age - t2.age
end,
--乘法
__mul = function(t1,t2)
end,
--除法
__div = function(t1,t2)
end,
--次方
__pow = function(t1,t2)
end,
--等于
__eq = function(t1,t2)
end,
--小于
__lt = function(t1,t2)
end,
--小于等于
__le = function(t1,t2)
end,
--连接符..
__concat = function(t1,t2)
end
}
mytable3 = {age = 10}
mytableclone = {age = 15}
setmetatable(mytable3,table3)
print(mytable3 + mytableclone)
print(mytable3 - mytableclone)
(4) __index方法
如果当前索引在子表中找不到索引对应的值,那么会去找__index所指定的表查找
table4 = {
age = 40,
}
mytable4 = {}
--__index所指定表,必须指定才能找到,否则返回nil
table4.__index = table4
setmetatable(mytable4,table4)
print(mytable4.age)
--rawget()从自身表中获取是否存在属性
print(rawget(mytable4,"age")) --没有这个属性返回nil
(5) __newIndex方法
当赋值的时候,如果赋值的索引为一个不存在的索引,那么这个值就会去__newindex所指的表赋值
table5 = {
age = 40
}
mytable5 = {}
table5.__newindex = table5
setmetatable(mytable5,table5)
mytable5.name = "卷"
print(mytable5.age) --不修改本身的表,所以打印为nil
print(table5.age)
print(table5.name)
--rawset()从自身表中设置属性
rawset(mytable5,"age",2)
print(mytable5.age)
八. 自带库
提供一些api方法,和其他语言一样,如mathf库 1.时间
print(os.time()) --查看系统时间
print(os.time({year= 2023,month= 9,day = 14})) --自己传入时间
local nowTime = os.date("*t") --获取当前时间,精确到秒
for k,v in pairs(nowTime) do
print(k,v)
end
print(nowTime.sec) --根据对象打印出指定时间
2.数学运算
print(math.abs(-11)) --绝对值
print(math.deg(math.pi)) --弧度转角度
--三角函数
print(math.cos(math.pi))
print(math.sin(math.pi))
--向上向下取整
print(math.ceil(3.4))
print(math.floor(3.4))
--最大值最小值
print(math.max(5,6))
print(math.min(5,6))
--小数的小数位分离
print(math.modf(6.8)) --6,0.8
--随机数 lua中的随机数必须要根据种子变化才会发生变化,种子树不变那不会随机
math.randomseed(os.time())
print(math.random(100))
print(math.random(100))
九. 面向对象
在lua语言中并不存在面向对象的概念,但是可以实现封装、继承、多态的特征。面向对象的类都是基于table来实现 1.封装
-- 定义一个类,id和Test方法
Object = {}
Object.id = 10
function Object:Test()
print("Test方法调用")
print(self.id)
end
-- 实现创建一个实例对象的方法
function Object:new()
local obj = {}
--需要使用到__index,当当前表中无值的时候就需要访问index指定的表
self.__index = self
setmetatable(obj,self)
return obj
end
--调用new方法,模拟实例化对象
local myobj = Object:new()
print(myobj.id)
myobj:Test()
-- 在新表中声明了id属性,那么调用的时候就是使用自己的表的id,不再是查找元表的id
myobj.id = 99
myobj:Test()
2.继承
function Object:subClass(className)
--使用大G表来添加类
_G[className] = {}
--写入继承需要的规则
local obj = _G[className]
self.__index = self
--创建一个base方法,让子类在重写父类方法的时候可以让父类的行为也保留下来
obj.base = self
setmetatable(obj,self)
end
-- 调用方法相当于继承了Object父类
Object:subClass("Person")
print(Person.id)
3.多态
相同的行为具有不同的表现
Object:subClass("GameObject")
GameObject.posX = 6
GameObject.posY = 8
function GameObject:Move()
self.posX = self.posX +1
self.posY = self.posY +1
print(self.posX)
print(self.posY)
end
GameObject:subClass("Student")
function Student:Move()
--相当于将父类表作为第一个参数传入方法中,应该避免父类表传入base方法
-- self.base:Move() 错误调用,相当于将父类表作为第一个参数传入方法中,不可以带入错误
self.base.Move(self) --正确写法
end
local p1 = Student:new()
p1:Move()
local p2 = Student:new()
p2:Move()