因為實習的項目用到了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,減少性能損耗