实际开发lua的IDE:vscode/IDEA/LuaStudio
学习用的:sublime text,轻便快捷
lua分号可以省略,打印函数print
ctrl+b运行
–单行注释,–[[中间写注释内容]]多行注释
--单行注释 print打印函数
--lua语句省略分号
--[[
ha
ha
]]
print("hello world")
print("童立华最帅")
变量:8种变量类型
–lua当中简单变量类型
–nil number string boolean
nil:空类型
number:数值类型,所有数值类型都是number
string:字符串,双引号和单引号都可以,没有char
boolean:是否
type函数:得到变量的类型,返回值是一个string
lua中可以使用没有初始化的变量,默认为nil
lua中不用指明变量类型,可以自动识别,可以随意给一个已经初始化的变量赋其他类型的值
–复杂数据类型
–函数function
–表 table
–数据结构 userdata
–协同程序 thread(线程)
print("******变量*******")
--lua当中简单变量类型
--nil number string boolean
--lua中所有的变量申明 都不需要申明变量类型 他会自动的判断类型
--类似C#的var
--lua中的一个变量可以随便赋值——自动识别类型
--通过type函数 我们可以得到变量类型
--type函数返回的是string
--lua中使用没有声明的变量不会报错,默认是nil
print(b)
print("******nil*******")
a = nil
print(a)
print(type(a))
print(type(type(a)))
--所有数值都是number
print("******number*******")
a = 1
print(a)
a = 1.2
print(a)
print("******string*******")
--字符串的声明使用单引号或者双引号,lua中无string
a = "123"
a = '123'
print(a)
print("******boolean*******")
a = true
print(a)
a = false
print(type(a))
print(a)
--复杂数据类型
--函数function
--表 table
--数据结构 userdata
--协同程序 thread(线程)
字符串操作
长度获取#
多行打印:转义字符以及[[]]
字符串拼接:…和string.format
字符串提供的公共方法
print("********字符串*********")
str = "双引号字符串"
str1 = '单引号字符串'
--获取字符串长度
print("********字符串长度*********")
s = "abc童立华"
--一个汉字占3个长度
--英文字符占1个长度
print(#s)
print("********字符串多行打印*********")
--lua中也是支持转义字符的
print("123\n123")
s = [[我是
童
立
华
]]
print(s)
print("********字符串拼接*********")
--字符串拼接通过..
print("123".."456")
--字符串类型和number类型也可以拼接,只要是..就是字符串拼接
s1 = "123"
s2 = 111
print(s1..s2)
print(string.format("我是童立华,我今年%d岁了",18))
--%d:与任何数字拼接
--%a:与任何字符拼接
--%s:与字符串配对
--.....
print("********别的类型转字符串*********")
a = true
print(tostring(a))
print("********字符串提供的公共方法*********")
str = "abCD"
--小写转大写,不会改变原字符串
print(string.upper(str))
--大写转小写
print(string.lower(str))
--翻转字符串
print(string.reverse(str))
--字符串索引查找,lua索引从1开始,返回2个值,起始位置和初始位置
print(string.find(str,"abC"))
--截取字符串,从第3个开始
print(string.sub(str,3))
print(string.sub(str,3,3))
--字符串修改
print(string.gsub(str,"CD","**"))
--字符转ASCII码
a = string.byte("Lua",1)
print(a)
--ASCII码转字符
a = string.char(a)
print(a)
运算符
算数运算符:+ - * /,也有幂运算^,和异或不同
条件运算符:其余一样,不等于是~=
逻辑运算符:&&对应and ||对应or !对应not
位运算符和三目运算符均不支持
print("******运算符*******")
print("******算数运算符*******")
-- + - * / %
--没有自增自减
--没有复合运算符 += -= *= %=
--字符串可以进行算术运算符操作,会自动转成number
print("加法运算"..1+2)
a = 1
b = 2
print(a+b)
print("123.4"+1)
print("减法运算"..1-2)
print("123.4"-1)
--因为lua只有number,所以没有向下取整
print("除法运算"..1/2)
--c++中是异或
print("幂运算"..2^3)
print("******条件运算符*******")
-- > < >= <= ~=
print(3>1)
print(3~=1)
print("******逻辑运算符*******")
-- && || !
--lua中对应的是and or not,也支持短路,如果是and,前面为false后面就不运行
print(true and false)
print(true or false)
print(not true)
print("******位运算符*******")
--不支持位运算,需要我们自己实现
print("******三目运算符*******")
--不支持三目运算符 ?:
条件分支语句
单分支语句:if 条件 then … end
双分支语句:if 条件then … else … end
多分支语句:if 条件 then … elseif 条件 then … elseif 条件 then … else … end
lua中没有switch语法
print("******条件分支语句*******")
a = 9
--if 条件 then ... end
--单分支
if a < 5 then
print("123")
end
--双分支
if a < 5 then
print("123")
else
print("321")
end
--多分支语句
if a < 5 then
print("123")
--lua中elseif一定要连着写
elseif a == 9 then
print("9")
end
if a >= 3 and a <= 9 then
print("3到9之间")
end
循环
while循环:while 条件 do … end
repeat-until循环:repeat … unitl 条件(注意是结束条件)
for循环:for 变量名 = 起始值,结束值,自定义增量(默认为+1) do … end
for循环是左闭右闭的
print("******循环语句*******")
print("******while语句*******")
--while 条件 do .... end
num = 0
while num < 5 do
print(num)
num = num + 1
end
print("******do-while语句*******")
-- repeat ... unitil 条件(注意是结束条件)
num = 0
repeat
print(num)
num = num+1
until num > 5 --满足条件跳出 结束条件
print("******for语句*******")
for i = 1,5 do -- lua里的i默认递增,默认加1
print(i)
end
for i = 1,5,2 do -- 如果要自定义增量 直接逗号后面写,如果要负数就直接把2改成-2
print(i)
end
函数(重点)(变长,嵌套闭包,多返回值)
两种声明方式:
- 一个在function后面取名字
- 一个不取名字,用一个变量来存储的
函数传参时:个数传多了,或者传少了不会报错。多了会丢弃,少了会补nil
函数返回值:可以有多个返回值,外部用多个变量来接取。接多接少不影响。少了丢弃,多了为nil
函数的类型为function
不支持函数重载
变长参数…:先用表接再使用
函数嵌套:就是函数里面声明函数。闭包:改变变量生命周期
print("******函数*******")
--function 函数名()
--end
--a = function()
--end
print("******无参数无返回值*******")
function F1()
print("F1函数")
end
--必须先声明再调用
F1()
--类似C#中的委托
F2 = function()
print("F2函数")
end
F2()
print("******有参数*******")
function F3(a)
print(a)
end
F3(1)
F3("123")
F3(true)
--如果传入的参数和函数类型不匹配,不会报错只会补空nil 或者 丢弃
F3()
F3(1,2,3)
print("******有返回值*******")
function F4(a)
return a,123,true
end
--多返回值时,在前面申明多个变量来接取即可
--如果变量不够 不影响 值接取对应位置返回值
--如果变量多了,会直接赋nil
tmp,tmp2,tmp3 = F4("123")
print(tmp)
print(tmp2)
print(tmp3)
print("******函数类型*******")
--函数的类型就是function
F5 = function()
print("123")
end
print(type(F5))
print("******函数重载*******")
--函数名相同,参数类型不同或者参数个数不同
--函数不支持重载,默认调用最后一个声明的函数
function F6()
print("童立华很帅")
end
function F6(str)
print("童立华"..str)
end
F6("很丑")
print("******变长参数*******")
function F7( ... )
--边长参数使用,用一个表表存起来,再用
arg = {...}
for i = 1,#arg do
print(arg[i])
end
end
--边长参数传进去的参数类型随意,个数也随意
F7(1,"123","456")
print("******函数嵌套*******")
function F8()
--[[F9 = function ()
print(123)
end
return F9]]
--也可以直接
return function()
print(456)
end
end
f9 = F8()
f9()
--闭包
function F9(x)
--改变传入参数的生命周期
return function(y)
return x + y
end
end
f10 = F9(10)
print(f10(5))
表table(重点)
表是一切复杂数据的基础:数组、二维数组、字典、类等
索引都是从1开始
中间为nil会断掉长度获取不准确
长度获取——#
自定义索引:计算长度时,忽略小于等于0的索引。
print("******复杂数据类型 table*******")
--所有的复杂类型都是table(表)
print("******数组*******")
a = {1,2,nil,true,nil}
--lua中索引从1开始
print(a[1])
print(a[3])
--#是通用的获取长度的关键字
--在打印长度的时候nil被忽略
--如果表中(数组中)某一位变成nil 会影响#获取的长度
print(#a)
print("******数组的遍历*******")
for i = 1,#a do
print(a[i])
end
print("******二维数组*******")
--其实是用表来体现数组和二维数组的特征
a = {{1,2},{3},{4,5}}
print(a[1][2])
print("******二维数组的遍历*******")
for i = 1,#a do
b = a[i]
for j = 1,#b do
print(b[j])
end
end
print("******自定义索引*******")
--不建议自定义索引
aa = {[0] = 1, 2, 3, [-1] = 4,5}
print(aa[-1])
print(#aa)
迭代器的遍历(重点)(ipairs和pairs区别)
for I, k in pairs(表名) do
end
ipairs:不能找到0和0以下的自定义索引的内容;如果从1开始索引顺序断了,后面的内容也找不到
pairs:建议使用它遍历各种不规则表,它可以得到所有信息
print("******迭代器遍历*******")
--迭代器遍历 主要是用来遍历表
--#得到长度 其实并不准确 一般不要用#来遍历表
a = {[0]=1,[2]=nil,[-1]=3,[5]=4,["s"]=5,[4]=6}
t={[2]=1,2}
print(#t)
print(#a)
print("******ipairs迭代器遍历*******")
--ipairs,i是索引,k是值
--ipairs遍历 还是从1开始遍历的 小于等于0的值得不到
--只能找到连续索引的 键 如果中间断续了 它也无法遍历出后面的内容
for i,k in ipairs(a) do
print("ipairs遍历剑指"..i.."_"..k)
end
print("******pairs迭代器遍历*******")
--它能把所有的键都找到 通过键可以得到值
for i,v in pairs(a) do
print("pair遍历剑指"..i.."_"..v)
end
for i in pairs(a) do
print("pairs遍历键"..i)
end
table实现字典,table实现类(重点)(点和冒号的区别,self),表的公共操作:表提供的公共方法
字典的本质也是表
访问可以通过中括号[键]访问;也可以通过字典名.键访问,数字不行
修改,直接访问后赋值
新增,直接加key赋值
删除,直接置为nil
遍历,用pairs
成员变量的申明:表内部声明或者表外部声明
成员函数的声明:和函数声明的两种方法相同,一种是变量的形式,另一种是function+名字的形式
在表内部函数想要访问表的变量或者方法时,必须要知道它们来自哪里
点和冒号:点是正常调用函数,冒号是把调用者作为第一个参数传入
冒号可以用来声明函数,只能是function+名字的形式声明,如果是冒号声明,相当于有一个默认参数
self就是用来冒号声明时作为第一个参数的代表的。self不是this!!!
print("******复杂数据类型 table2*******")
print("******字典*******")
--字典使用键值对构成的
--通过自定义索引来实现字典(c++的map)
a = {["name"] = "童立华",["age"] = 14, ["1"] = 5}
--访问单个变量就是 用中括号 填键来访问
print(a["name"])
--还可以通过类似.成员变量的形式得到值
print(a.name)
--修改
a["name"] = "TLH"
print(a["name"])
--新增
a["sex"] = false;
print(a["sex"])
--删除
a["sex"] = nil
print(a["sex"])
print("******字典的声明*******")
print("******字典的遍历*******")
--如果要遍历字典一定要用pairs
for k,v in pairs(a) do
print(k.."_"..v)
end
--可以只遍历键,但是不可以只遍历值
for k in pairs(a) do
print(k.."_"..a[k])
end
print("******类和结构体*******")
--lua中是没有面向对象的,需要我们自己实现
--成员变量,成员函数
Stuendt = {
--类似自定义索引,记得用逗号隔开
--年龄
age = 1,
--性别
sex = true,
--成长函数
Up = function()
--这样写 这个age 和表中的age没有任何关系 它是一个全局变量
--print(age)
--想要在表内部函数中,调用表本身的属性或者方法
--一定要指明是谁的 所以 表名.属性 或者 表名.方法
print(Stuendt.age)
print("我成长了")
end,
--学习函数
Learn = function(t)
--第二种 能够在函数内部调用自己属性或者方法的 方法
--把自己作为一个参数传进来 在 内部访问
print(t.sex)
print("好好学习天天向上")
end
}
--申明表过后,从表外取申请表有的变量和方法
Stuendt.name = "帅华"
Stuendt.Speak = function()
print("说话")
end
--函数的第三种申明方式
function Stuendt:Speak2()
--lua中 有一个关键字 self 表示 默认传入的第一个参数
print(self.name.."说话")
end
--C#要使用类,实例化对象new 静态直接.
--lua中类的表现 更像是一个类中有很多 静态变量和函数
print(Stuendt.age)
print(Stuendt.name)
Stuendt.Up()
Stuendt.Speak()
Stuendt:Speak2()
Stuendt.Speak2(Stuendt)
--lua中点和冒号的区别
Stuendt.Learn(Stuendt)
--冒号调用方法 会默认把调用者 作为第一个参数传入方法中
Stuendt:Learn()
print("******表的公共操作*******")
--表中 table提供的一些公共方法的讲解
t1 = {{age =1, name = "123"},{age = 2, name = "345"}}
t2 = {name = "童立华", sex = true}
--插入
print(#t1)
--将t2插到t1后面
table.insert(t1,t2)
print(#t1)
print(t1[1])
print(t1[2])
print(t1[3].sex)
--删除指定元素
--remove方法 传表进去 会移除最后一个索引的内容
table.remove(t1)
print(#t1)
print(t1[1].name)
--remove方法,传两个参数 是要移除内容的表
--第二个参数,是要移除内容的索引
table.remove(t1,1)
print(t1[1].name)
t2 = {5,2,7,9,5}
table.sort(t2)
--最好不要用长度遍历
for _,v in pairs(t2) do
print(v)
end
--第一个是用于排序的表,第二个是排序规则函数,false就交换
table.sort(t2,function(a,b)
if a > b then
return true
else
return false
end
end)
for _,v in pairs(t2) do
print(v)
end
--拼接
tb = {"123", "456", "789", "1011"}
--用于拼接表中元素 返回值是一个字符串
str = table.concat(tb,";")
print(str)
多lua脚本的执行
全局变量和局部变量:局部变量就是前面用local修饰
多脚本执行:require(“脚本名”)
脚本的最后可以return一个外部希望获取的内容一般情况是一个局部变量
脚本卸载:package.loaded[“脚本名”]如果返回true证明已经加载过了
package.loaded[“脚本名”] = nil就是卸载脚本
大
_G表是一个总表(table) 它将我们声明的所有全局变量都存储在其中
本地变量加了local的变量不会存到大_G表
print("******多脚本执行*******")
print("******全局变量和本地变量*******")
--全局变量
a = 1
b = "123"
for i = 1,2 do
c = "童立华"
end
print(c)
--本地(局部)变量的关键字
for i = 1,2 do
local d = "童立华"
print("循环中的d".."_"..d)
end
print(d)
fun = function ( )
local tt = "1231234"
end
fun()
print(tt)
local tt2 = 1
print(tt2)
print("******多脚本执行*******")
--关键字 requeire("脚本名") require("脚本名")
require("test")
print(testA)
print(testlocalA)
print("******脚本卸载*******")
--如果是require加载执行的脚本,加载一次过后不会再被执行
require("test")
--package.loaded["脚本名"]
--返回值是boolean,意思是 该脚本是否被执行
print(package.loaded["test"])
--卸载已经执行过的脚本
package.loaded["test"] = nil
print(package.loaded["test"])
--require 执行一个脚本时 可以在脚本最后返回一个外部希望获取的内容
local testLA = require("test")
print(testLA)
print("******大G表*******")
--_G表是一个总表(table) 它将我们声明的所有全局变量都存储在其中
for k,v in pairs(_G) do
print(k,v)
end
--本地变量 加了local的变量不会加入到大_G表中
特殊用法(重点)(多变量赋值,三目运算符)
多变量赋值:多变量赋值,如果后面的值不够,会自动补空。如果后面的值多了会自动省略
多返回值:多返回值,你用几个变量接 就有几个值。如果少了 就少接几个 如果多了 就自动补空
and和or的特殊用法:and和or可以模拟三目运算符
逻辑与 逻辑或
and or 它们不仅可以连接 boolean 任何东西都可以用来连接
在lua中 只有nil和false才认为是假
"短路"对于and来说 有假则假 对于or来说 有真则真
所以 它们只需要判断 第一个是否满足 就会停止计算了
print("******特殊用法*******")
print("******多变量赋值*******")
--多变量赋值,如果后面的值不够,会自动补空
--如果后面的值多了会自动省略
a,b,c = 1,2
print(a)
print(b)
print(c)
print("******多返回值*******")
function Test()
return 10,20,30,40
end
--多返回值,你用几个变量接 就有几个值
--如果少了 就少接几个 如果多了 就自动补空
a,b,c = Test()
print(a)
print(b)
print(c)
a,b,c,d,e = Test()
print(a)
print(b)
print(c)
print(d)
print(e)
print("******and or*******")
--逻辑与 逻辑或
--and or 它们不仅可以连接 boolean 任何东西都可以用来连接
--在lua中 只有nil和false才认为是假
--"短路"对于and来说 有假则假 对于or来说 有真则真
--所以 它们只需要判断 第一个是否满足 就会停止计算了
print(1 and 2)
print(nil and 1)
print(false and 1)
print(true and 1)
print(true or 1)
print(nil or 1)
--lua不支持三目运算符
x = 3
y = 2
-- ? :
local res = (x>y) and x or y
print(res)
--(x>y) and x,and是有假则假,要一直找到假的,所以返回x
--x or y,是有真则真,所以返回x
--(x>y) and y,and是有假则假,x>y已经是false了,直接返回x>y
--(x>y) or y,or是有真则真,所以返回y
协同程序
1.创建协同程序:两种方式
coroutine.create(函数)的返回值是一个thread对象;cotoutine.wrap(函数)返回值是一个函数
2启动协程
cotoutine.resume(协程对象),默认返回值第一个为是否开启成功;直接调用函数(wrap创建的)
3. 协程挂起:cotoutine.yield()
4. 协程的状态 coroutine.status(协程对象或者协程函数):dead,suspended,running
print("******协同程序*******")
print("******协程的创建*******")
--常用方式
--coroutine.create()
fun = function ( )
print(123)
end
co = coroutine.create(fun)
co = coroutine.create(function()
print(456)
end)
--协程的本质是一个线程对象
print(co)
print(type(co))
--coroutine.wrap()
co2 = coroutine.wrap(fun)
print(type(co2))
print("******协程的运行*******")
--第一种方式 对应的 是通过create创建的协程
coroutine.resume(co)
--第二种方式
co2()
print("******协程的挂起*******")
fun2 = function()
local i = 1
while true do
print(i)
i = i + 1
--协程的挂起函数!!
--直到下次重启协程,才取消挂起
print(coroutine.status(co3))
coroutine.yield(i)
end
end
co3 = coroutine.create(fun2)
--默认第一个返回值是协程是否启动成功
--第二个返回值是yield里面的返回值
isOK,tempI = coroutine.resume(co3)
print(tempI)
coroutine.resume(co3)
co4 = coroutine.wrap(fun2)
--这种方式的协程调用 也可以有返回值 只是没有默认的第一个返回值了
print(co4())
co4()
co4()
print("******协程的状态*******")
--coroutine.status(协程对象)
--dead 结束
--suspended 暂停
--running 进行中
print(coroutine.status(co3))
print(coroutine.status(co))
--这个函数可以得到当前正在运行的协程的线程号
print(coroutine.running(co))
元表
1.元表的概念
–任何表变量都可以作为另一个表变量的元表
–任何表变量都可以有自己的元表(爸爸)
–当我们子表中进行一些特定操作时
–会执行元表中的内容
2.设置元表,得到元表
setmetatable(子表,元表)
getmetatable(子表)
特定操作
__tostring
__call
运算符重载
__index
__newindex
raw
rawget:绕开__index
rawset:绕开__newindex
print("******元表*******")
print("******元表的概念*******")
--任何表变量都可以作为另一个表变量的元表
--任何表变量都可以有自己的元表(爸爸)
--当我们子表中进行一些特定操作时
--会执行元表中的内容
print("******设置元表*******")
meta = {}
myTable = {}
--设置元表函数
--第一个参数 子表
--第二个参数 元表(爸爸)
setmetatable(myTable,meta)
print("******特定操作*******")
print("******特定操作-_tostring*******")
meta2 = {
--当子表要被当做字符串使用时 会默认调用这个元表中的tostring方法
__tostring = function(t)
return t.name
end
}
myTable2 = {
name = "童立华2"
}
setmetatable(myTable2,meta2)
print(myTable2)
print("******特定操作-__call*******")
meta3 = {
--当子表要被当做字符串使用时 会默认调用这个元表中的tostring方法
__tostring = function(t)
return t.name
end,
--当子表作为一个函数来使用时,会默认调用这个__call的内容
--当希望传参数时 一定要记住 默认第一个参数 是调用者自己
__call = function (a,b)
print(a)
print(b)
print("童立华好帅")
end
}
myTable3 = {
name = "童立华2"
}
setmetatable(myTable3,meta3)
--把子表当做函数使用,就会调用元表的__call方法
myTable3(1)
print("******特定操作-_运算符重载*******")
meta4 = {
--相当于运算符重载 当子表使用+运算符时,会调用该方法
__add = function(t1,t2)
return t1.age + t2.age
end,
__eq = function(t1,t2)
return t1.age == t2.age
end
}
myTable4 = {age = 1}
setmetatable(myTable4,meta4)
myTable5 = {age = 1}
setmetatable(myTable5,meta4)
print(myTable4 + myTable5)
--如果要用条件运算符 来比较两个对象
--这两个对象的元表一定要一致 才能准确调用方法
print(myTable4 == myTable5)
print("******特定操作-_index和_newIndex*******")
meta6 = {
age = 1
}
meta6.__index = meta6
myTable6 = {}
setmetatable(myTable6,meta6)
--获取元表
print(getmetatable(myTable6))
--__index 当字表中找不到某一个属性时
--会到原表中 __index指定的表去找属性
print(myTable6.age)
--newIndex 当赋值时,如果赋值一个不存在的索引
--那么会把这个值赋值到newIndex所指的这个表中 不会修改自己
meta7 = {
}
meta7.__newindex = {}
myTable7 = {}
setmetatable(myTable7,meta7)
myTable7.age = 1;
print(myTable7.age)
print(meta7.__newindex.age)
--rawset该方法 会忽略元表的newindex
--rawset(myTable7,"age",2)
--print(myTable7.age)
lua面向对象之封装(重点!!),lua面向对象之继承(重点!!),lua面向对象之多态(重点!!)
表就是表现类的一种形式
实现了new的方法:本质上是创建了一个空表,主要用到了元表和__index。
修改创建出来的对象的属性:本质是为这个空表对象新建了一个成员属性(变量)
冒号:self代表函数调用者
写了继承方法:_G根据字符串创建一个新的表(类)
元表和__index相关知识点 类达到了一个继承的表现
多态就是相同方法名,子类有不同的处理逻辑
直接重写这个方法
如果想要保留父类逻辑执行:在继承时加入自定义base属性,在subClass方法中赋值
通过base调用父类方法时,一定记住 不要用冒号调用;通过.自己穿第一个参数进入父类函数内部
print("******面向对象*******")
print("******封装*******")
--面向对象 类 其实都是基于table来实现
--元表相关的知识点
Object = {}
Object.id = 1
function Object:Test()
print(self.id)
end
--冒号 时会自动将调用这个函数的对象 作为第一个参数传入的写法
function Object:new()
--sele不是this,代表的是我们默认传入的第一个参数
--对象就是变量
--返回出去的内容 本质上就是表对象
local obj = {}
--结合元表的知识 __index 当找自己的变量 找不到时 就会去找元表当中__index指向的内容
self.__index = self
setmetatable(obj, self)
return obj
end
local myObj = Object:new()
print(myObj.id)
--找不到用别人,找得到就用自己的了
myObj:Test()
--相当于对空表中申明一个新的属性 叫做id
myObj.id = 2
myObj:Test()
print("******继承*******")
--C# class 类名 : 继承类
--写一个用于继承的方法
function Object:subClass(className)
-- _G知识点 是总表 所有声明的全局变量 都以键值对的形式存在其中
_G[className] = {}
--写相关继承的规则
--用到元表
local obj = _G[className]
self.__index = self
--因为是父类调用该函数,所以将self设为元表
--给子类定义一个base属性 base属性代表父类
obj.base = self
setmetatable(obj,self)
end
--[[print(_G)
_G["a"] = 1
_G.b = "123"
print(a)
print(b)--]]
--subclass就是设置自己的子类
Object:subClass("Person")
local p1 = Person:new()
--这就是改p1这张空表里的id属性,不会影响上层元表中的id
--然后要输出时,如果没有就找元表中的__index指向的表,如果有就用自己的
p1.id = 100
print(p1.id)
Object:subClass("Monster")
local m1 = Monster:new()
m1.id = 200
print(m1.id)
print("******多态*******")
--相同行为不同表现 就是多态
--相同方法 不同执行逻辑 就是多态
Object:subClass("GameObject")
GameObject.posX = 0
GameObject.posY = 0
function GameObject:Move()
self.posX = self.posX+1
self.posY = self.posY+1
print(self.posX)
print(self.posY)
end
GameObject:subClass("Player")
function Player:Move()
--可以通过base.move()
--base指的是 GameObject 表(类)
--这种方式调用 相当于是把基类表 作为第一个参数传入了方法中
--避免把基类表 传入到方法中 这样相当于就是共用一张表的属性了
--如果要执行父类逻辑 不要直接使用冒号调用
--要通过.调用 然后自己传入第一个参数
self.base.Move(self)
end
local p1 = Player:new()
p1:Move()
--目前这种写法有坑 不同对象使用的成员变量 是相同的成员变量
--不是自己的
local p2 = Player:new()
p2:Move()
自带库
os,math,package
print("******自带库*******")
print("******时间*******")
--系统时间
print(os.time())
--自己传入参数得到时间
print(os.time({year = 2020, month = 2, day = 21}))
--os.date("*t")
local nowTime = os.date("*t")
for k, v in pairs(nowTime) do
print(k,v)
end
print(nowTime.hour)
print("******数学运算*******")
--math
--绝对值
print(math.abs(-11))
--弧度转角度
print(math.deg(math.pi))
--三角函数
print(math.cos(math.pi))
--向下向上取整
print(math.floor(2.6))
print(math.ceil(4.2))
print(math.max(1,2))
--小数分离,分成整数部分和小数部分
print(math.modf(1.2))
--随机数
--先设计随机数种子
math.randomseed(os.time())
print(math.random(100))
--开方
print(math.sqrt(100))
print("******路径*******")
--lua脚本加载路径
print(package.path)
lua垃圾回收
关键字——collectgarbage(“命令”)
命令:count:获取内存数KB,collect:进行一次完整的垃圾回收
print("******垃圾回收*******")
test = {id = 1, name = "hua"}
--垃圾回收关键字
--collectgarbage
--获取lua占用内存数 K字节 用返回接*1024 就可以得到具体的内存占用字节数
print(collectgarbage("count"))
--置空就是清空这个对象
test = nil
--进行垃圾回收 有点像C#的GC
collectgarbage("collect")
print(collectgarbage("count"))
可能会问的问题
-
lua如何实现面向对象
lua实现面向对象主要也就是实现封装,继承和多态。
封装的话主要使用table来实现类,然后实现new方法,本质就是在new内部创建了一个空表,最后就是返回这个空表作为new出来的对象。将这个空表的元表设为以类名创建的那个表,然后将元表的_index设为元表本身,这样在对象中找不到的属性就会去元表中找。
继承的话是写一个用于继承的函数,我的办法是写一个父表的函数subclass,参数是子类的类名,在大_G表中根据这个子类名创建一个table,将这个table赋给内部创建的空表,还是用到了元表和_index的办法,将这个函数中将元表设为父类,将父类的_index设为自己。
多态就是相同的方法名,子类有不同的处理逻辑,直接重写父类的方法,如果想要保留父类方法的基础上再加上自己的处理,就在继承的函数中为子类加上base属性,将base设为self,也就是设置自己的父类,然后在重写的方法中先写上self.base.函数名(self)就行了。 -
lua元表是什么,有什么作用
为了关联两个表,解决无法对两个table进行操作的问题,lua提供了元表metatable,可以用setmetable函数将一个表设为另一个表的元表。
任何表变量都可以作为另一个表的元表,任何表变量都可以有自己的元表。
当我们在子表中进行一些特定操作时,会执行元表中的内容,比如__index元方法,__newindex这些,还有__tostring元方法和__call元方法。通过为table设置元表可以在lua中实现面向对象。
√
__call元方法,把子表当做函数来使用的时候,会默认调用__call的内容。
__tostring元方法,把子表要当做字符串使用时,会默认调用__tostring方法
-
lua中的userdata是什么
userdata是一种用户自定义数据,用于表示一种应用程序或者C/C++语言库所创建的类型,可以将任意C/C++的任意数据类型(通常是struct和指针)存储到lua变量中调用,具体的话我也没有用过。 -
lua中ipairs和pairs的区别
ipairs和pairs都是lua默认提供的迭代函数
ipairs从索引为1开始遍历,不能找到0和0以下的自定义的索引的内容,遇到nil也会终止遍历,只能找到连续索引的键,如果中间断了,它也无法遍历出后面的内容
pairs:建议使用它遍历各种不规则表,它可以得到所有信息 -
lua的闭包如何实现
在lua中,闭包是由一个函数和该函数会访问到的非局部变量组成的,其中非局部变量是指不是在局部作用范围定义的一个变量,同时又不是一个全局变量。在lua中主要通过函数嵌套来实现,在c++中的话就通过lambda来实现。
举个栗子就是
function func(x)
return function(y)
return x+y
end
end
然后f = func(10),f就是一个闭包
f(5)的结果就是15 -
lua的GC
lua采用了自动内存管理,所以不用操心新创建的对象需要的内存如何分配出来,也不用考虑在对象不再被使用后怎么样释放它们所占用的内存。当一个变量不再使用,或者我们在变量不被引用的前提下主动将变量置为nil,就可以等待lua垃圾收集器以指定的垃圾收集器间歇率和垃圾收集器步进倍率来回收无用的数据。
常用的垃圾回收函数有:
collectgarbage(“collect”)做一次完整的垃圾收集循环。
collectgarbage(“count”)获取lua占用内存数,单位是K字节,乘上1024就可以得到具体的内存占用字节数。 -
require原理
为了方便代码管理,通常把lua代码分成不同的模块,然后再通过require函数把它们加载进来。
1require会搜索目录加载文件
2require会判断是否文件已经加载避免重复加载同一文件
原理:
好像记得lua require的默认加载路径是lua系统封装的两个全局属性,package.path和cpath。
√ -
_index和_newindex作用
_newindex元方法用来对表进行更新,_index用来对表进行访问。
_index:当子表中找不到某个属性时,会到元表中,_index指定的表去找该属性
_newIndex:当赋值时,如果赋值一个不存在的索引(键),那么会把这个值赋值到newindex所指的表中,不会修改自己。如果
对已存在的索引键,则会进行赋值,而不调用元方法_index -
lua table怎么判空
可以用lua内置的next函数,用if next(tableName) == nil then。
next相当于取pairs遍历该table的下一个内容的函数,如果为空,证明这个table就是空的 -
lua实现类
用table实现类,来描述对象的属性,用function来表示类函数,所以lua中的类可以通过table+function模拟出来。
先以你想要的类名定义一个table,然后写一个new函数,用table:new来定义,用冒号定义在调用这个函数的时候,将调用函数的对象作为第一个参数传入进去。在new里面再定义一个table,将这个table返回出去,作为new出来的对象。将这个对象的元表设为以类名定义的表,也就是函数的调用对象self。把self的_index也设为self,这样在对象自身这个表格找不到的时候就会去元表中找。
在访问属性的时候用点.访问,调用函数的时候用:调用。
还有继承和多态这些就比较复杂了。 -
lua协程
协程就是协同程序,lua协程与线程比较类似:有独立的堆栈,独立的局部变量,独立的指令指针,同时又与其他协程共享全局变量和大部分东西,协程的本质是一个线程对象,但是和线程不同的是在某一时刻只有一个协程在运行,而且正在运行的协程只有明确的被要求挂起的时候才会被挂起。用corutine.create函数或者wrap函数来创建协程,用yield来挂起协程,用resume函数来启动协程。
√ -
lua的string,lua连接两个字符串的底层操作
lua的字符串可以用一对双引号或者单引号来表示,用”…”来连接 ,也可以用table.concat函数来实现字符串拼接,底层原理我不是很清楚,不好意思。 -
lua的#作用
#可以获取字符串的长度,但是对于table要注意:
如果是用table模拟的数组,如果中间某个是nil,那么对这个table进行#,就只会求nil前的长度。
如果是table,lua中的table可以用数字或者字符串作为键,#得到的是用整数作为索引的最开始连续部分的大小。
√ -
lua手写n!
用递归,写清楚递归边界和递归式,递归边界当n==1返回1
function factorial(n)
if n == 1 then
return 1;
else
return n * factorial(n-1)
end
end
√ -
lua的弱表:
当把对象放入表中时就会产生一个引用,即使其他地方没有对表中的元素有任何引用,垃圾收集器也不会回收这些对象,你只能手动释放元表或者将其常驻内存。用弱表就是告诉lua一个引用不应该阻碍一个对象被回收,弱引用是一种会被垃圾收集器忽视的对象引用,如果一个对象的所有引用都是弱引用,lua就可以回收这个对象了。lua使用弱表实现弱引用,弱表是一个表,它有metatable,并且metatable定义了__mode字段,一个表的弱引用类型由__mode模式字段来决定的,取值有k和v以及kv,k代表table的键是弱引用的,也就是table的key能够被垃圾收集器自动回收。kv是二者的组合,只要key和value一个被回收,那么kv键值对就被从表中移除。