熱更新語言--lua學習筆記

movin2333發表於2021-04-03

一.lua安裝和程式設計環境搭建

  lua語言可以在官網:http://luadist.org/下載安裝包安裝,程式設計IDE之前學習使用的是SciTE(https://www.cnblogs.com/movin2333/p/14348703.html),這個IDE安裝時會一併安裝lua,當然,vscode、idea、luaStudio等IDE也可以編寫lua,這次使用SublimeText編寫lua,SublimeText的內地官網:http://www.sublimetext.cn/

二.lua語法:變數

  1.簡介:lua中的變數類似於c#種var,使用時不需要宣告變數型別,賦值時確定變數型別。沒有宣告的變數可以直接使用,預設值為nil。

  2.變數分類

    1)簡單變數型別 nil number string boolean

    2)複雜資料型別 function table userdata thread

  3.簡單變數型別

    1)nil:空值

    2)number:數值,所有的數值型別(不管是整型還是浮點型)都是number型別

    3)string:字串,宣告字串使用單引號或者雙引號都可以

    4)boolean:布林值

    5)和型別相關的函式:type(xxx)--獲取變數xxx的型別

  4.string型別的相關函式

    1)使用#獲取字串長度:

str = "雙引號字串"
str2 = '單引號字串'
--獲取字串的長度
print(#str)

    注意:一個漢字佔用3個位元組(utf-8編碼)

    2)字串多行列印

--字串多行列印
--lua中支援轉義字元
print("123\n123")
--定義多行字串
s = [[你好
我的寶貝
歡迎來到
lua]]
print(s)

    3)字串拼接

str = "雙引號字串"
str2 = '單引號字串'

--字串拼接,通過..拼接
print(str..str2)
s1 = "123"
s2 = 1232
s3 = true
--print(s1..s2..s3)  報錯,boolean值不能拼接
print(s1..s2)

    注意:boolean值拼接時報錯。

--字串拼接,通過format函式拼接
print(string.format("你好,我今年%d歲,尚未婚配",18))
--%d:配對數字
--%a:配對字母
--%s:配對字元

    4)其他型別轉字串

--非字串型別轉字串
print(tostring(true))
--使用tostring函式,直接列印預設也會呼叫tostring函式(和C#其他型別轉字串方式是一致的)
print(true)

    5)字串的其他函式

--其他字串函式
s = "addDEIN"
--小寫轉大寫
print(string.upper(s))
--大寫轉小寫
print(string.lower(s))
--反轉字串
print(string.reverse(s))
--字串索引查詢
print(string.find(s,"ddD"))
--擷取字串
print(string.sub(s,3,5))
--字串重複
print(string.rep(s,3))
--字串修改
print(string.gsub(s,"dd","**"))
--字元轉ASCII碼
a = string.byte("lua",2)
print(a)
--ASCII碼轉字元
print(string.char(a))

    注意:lua中有多返回值,如查詢函式中返回值為“ddD”字串在s中從第2個字元到第4個字元,替換字串中第二個返回值1代表替換了1次。lua中字串索引是從1開始的,而不是0(和C#不同)。

三.lua語法--運算子

  1.算數運算子:支援+、-、*、/、%,不支援自增++自減--複合運算子+=、-=、*=、/=、%=。注意:在lua中+號沒有拼接字串功能,使用..進行字串拼接,將數字字串和數字使用+號連線時系統會自動將字串轉為number型別進行加法運算。

        lua中支援c#中沒有的冪運算,使用^符號(如3的4次冪記為3^4),但是這個符號在c#中是冪運算子。

  2.條件運算子:支援>、<、>=、<=、==、~=(不等於),注意:不等於符號和c#不同。

  3.邏輯運算子:and(與)、or(或)、not(非),注意:lua中邏輯運算子和c#符號不同,但是功能幾乎相同,都支援短路。

  4.lua中不支援位運算和三目運算

四.lua語法--條件分支語句

  1.if語句

--條件語句
a = 2

--單分支
if a < 3 then
    print("a<3")
end

--雙分支
if a < 4 then
    print("a<4")
else
    print("a>=4")
end

--多分支
if a < 5 then
    print("a<5")
elseif a == 5 then
    print("a=5")
else
    print("a>5")
end

  2.lua中沒有switch語法和三目運算子。

五.lua語法--迴圈語句

--迴圈語句

--while迴圈
num = 0
while num < 5 do
    print(num)
    num = num + 1
end

print("***********************")
--do while迴圈
--注意:until後面是結束迴圈的條件,而c#中do while語句while後是繼續迴圈的條件,這一點不同
num = 0
repeat
    print(num)
    num = num + 1
until num > 5

print("************************")
--for迴圈
--預設自增1,i從1自增到大於5結束
for i = 1,5 do
    print(i)
end
--指定i自增2,i從1自增到大於5結束
for i = 1,5,2 do
    print(i)
end

六.lua語法--函式

  1.無參無返回值

--函式

--無參無返回值
--函式定義方式一
function F1()
    print("F1函式")
end
--函式定義方式二,類似C#中的委託
F2 = function()
    print("F2函式")
end

F1()
F2()

  注意:lua中程式碼從上向下執行,如果函式呼叫時還未定義,而呼叫完函式後定義函式,會報錯。這一點和C#不同,因為C#會預先編譯,因此函式先呼叫再定義是沒有問題的,但是lua不會編譯,不能先呼叫再定義。

  2.有引數,不用指定引數型別

--函式

--有引數
F1 = function(a)
    print(a)
end

F1(345)

  注意:如果函式定義了引數,但是不給定引數或者少給了多給了引數,都不會報錯,lua會自動補空nil或者丟棄

  3.有返回值

--函式

--有返回值
--函式可以任意返回多個返回值,使用逗號隔開
F1 = function()
    return 1,2,3,"true"
end

print(F1())
--使用多個變數接多返回值,變數個數和返回值個數不統一時也會補空或者丟棄返回值
a,b,c,d = F1()
print(a..b..c..d)

  4.lua中函式的型別是複雜資料型別function。lua不支援函式的過載,預設呼叫最後宣告的函式(宣告過載函式相當於改變了函式賦值)

  5.變長引數

--函式

--變長引數
F1 = function(...)
    arg = {...}
    for i = 1,#arg do
        print(arg[i])
    end
end

F1(1,2,3,4,5,6,7,1,2,12,1)

  6.函式巢狀

--函式

--函式巢狀
F1 = function()
    return function()
        print(1234)
    end
end

--返回函式時可以使用一個變數來接收返回函式再呼叫
F2 = F1()
F2()
--也可以直接使用兩個括號呼叫,第一個括號呼叫F1,第二個括號呼叫F1()返回的函式
F1()()


--閉包
F3 = function(x)
    --x作為引數本來是一個臨時變數,但是在返回函式中被呼叫,改變了x的生命週期,稱為閉包
    return function(y)
        return x + y
    end
end

print(F3(2)(3))

七.lua語法--表

  1.概述:所有的複雜型別都是基於table(表)實現的。

  2.陣列及陣列遍歷

--陣列

--定義陣列,任意型別的資料都可以放到同一個表中
a = {1,2,3,"字串",nil,true,nil}
--lua中陣列索引從1開始,所以索引0值為nil
print(a[0])
print(a[1])
print(a[4])
--#是通用的獲取長度的關鍵字,注意:計算長度時,從第一個nil開始的所有資料會被忽略,因此這裡計算出來的陣列長度為4,取陣列長度時true和nil都沒有計算在內
print(#a)

--遍歷
--通過#遍歷陣列是一種不可靠的遍歷,這裡就沒有遍歷到true
for i = 1,#a do
    print(a[i])
end

  3.二維陣列及其遍歷

--二維陣列

a = {{1,2,3},{4,5,6}}
--取值
print(a[1][2])
--遍歷
for i = 1,#a do
    for j = 1,#a[i] do 
        print(a[i][j])
    end
end

  4.自定義索引

--自定義索引

a = {[0] = 1,2,3,[-1] = 4,5}
--取值
print(a[0])
print(a[-1])
--獲取長度時實際上從下標1開始獲取元素個數,所以這裡得到的長度是3
print(#a)

--自定義索引跳過其中某些索引出現的異常情況
m = {[1] = 1,[2] = 2,[4] = 4,[6] = 6}
print(#m)  --實際有4個元素,索引值3和5對應的值是nil,但是實際得到的元素個數是6
n = {[1] = 1,[2] = 2,[5] = 5,[6] = 6,[9] = 9,[10] = 10}
print(#n)  --實際有6個元素,有兩處跳過了連續兩個索引3、4和7、8,但是實際得到的元素個數是2

  5.迭代器遍歷

--迭代器遍歷
--#得到的表的長度不準確,使用#遍歷不推薦使用,一般使用迭代器遍歷

a = {[0] = 1,2,[-1] = 3,4,5}

--ipairs遍歷,仍然是從1開始遍歷,也只能找到連續索引值,索引值斷開會出現遍歷不準確的問題
for i,k in ipairs(a) do
    print(i..":"..k)
end

print("******************")

--pairs遍歷,能遍歷出所有值,推薦使用的遍歷方法
for i,v in pairs(a) do
    print(i..":"..v)
end

print("******************")
--pairs遍歷,只遍歷鍵的方法
for i in pairs(a) do
    print(i)
end

  6.使用table實現字典功能

--字典

--使用自定義索引的方式就可以自定義表
a = {["name"] = "movin",["age"] = 14,["sex"] = true,["1"] = 2}

--使用類似於C#索引器的方式訪問字典
print(a["name"])
print(a["age"])
print(a["sex"])
print(a["1"])

print("******************")
--使用類似於成員變數的方式訪問
print(a.name)
print(a.age)
--print(a.1)會報錯,使用這種方式訪問時索引不能是數字

print("******************")
--修改和新增,直接賦值即可,有這個索引就是修改,沒有這個索引就是新增
a["sex"] = false
print(a["sex"])
print(a.sex)
a["mm"] = 14
print(a["mm"])
print(a.mm)

print("******************")
--刪除,置空即可
a["age"] = nil
print(a["age"])

print("******************")
for k,v in pairs(a) do
    print(k,v)
end

  7.利用表模仿類的實現

    1)基本宣告

--

--lua中預設沒有物件導向,但是我們可以使用表來表現類
Student = {
    --宣告變數age
    age = 1,
    --宣告變數sex
    sex = false,

    --成員函式Up
    Up = function()
        print("我成長了")
    end,
    --成員函式Study
    Study = function()
        print("我在學習")
    end,
}

--lua中類的屬性和方法更類似於靜態的屬性和方法
print(Student.age)
Student.Up()
Student.Study()

    2)表內的函式使用表內的其他元素

--

Student = {
    age = 1,
    sex = false,

    Up = function(s)
        --在表中使用表內的變數和方法,不能直接呼叫如print(age),這樣訪問不到表中的變數age
        --可以通過表名點出呼叫的變數
        print(Student.age)
        --可以將自己作為引數傳遞,然後呼叫
        print(s.name)
    end,

    Study = function()
        print("我在學習")
    end,
}

--在表的外部為表新增屬性或方法
Student.name = "movin"
print(Student.name)

--表的外部呼叫函式Up,將表自身作為引數傳遞
Student.Up(Student)
--使用冒號可以簡寫,冒號可以將自身作為第一個引數傳遞。
Student:Up()


--注意:定義函式時也可以使用冒號進行定義,使用self作為自身
 function Student:Up2()
    print(self.sex)
end

Student:Up2()

   8.表的一些公共操作

t1 = {{age = 1,name = "123"},{age = 2,name = "345"}}
t2 = {name = "movin",sex = true}

--表的插入
print(#t1,#t2)
table.insert(t1,t2)  --將t2插入t1最後
print(#t1,#t2)

--刪除指定元素
--預設remove函式移除表中最後一個值
print(t1[1])
print(t1[2])
print(t1[3])
table.remove(t1)
print(t1[1])
print(t1[2])
print(t1[3])
--指定移除表中某個元素
table.remove(t1,1)
print(t1[1])
print(t1[2])
print(t1[3])

--表的排序
t3 = {1,3,8,-1,0,5}
--預設升序排序
table.sort(t3)
for _,v in pairs(t3) do
    print(v)
end
--自定義排序規則,類似於C#中list的sort函式的使用,這裡實現降序排序
table.sort(t3,function(a,b)
    if a>b then
        return true  --這個返回值可以理解為a和b是否交換位置
    end
end)
for _,v in pairs(t3) do
    print(v)
end

--表的拼接
t4 = {"123","456","789","111"}
--將表中元素使用第二個引數的符號連線成一個字串
print(table.concat(t4,","))

 八.lua語法--多lua指令碼執行

  1.全域性變數和區域性變數

    不適用local宣告的變數稱為全域性變數,在整個指令碼中都存在(無論在哪裡宣告);使用local宣告的變數是區域性變數,只在當前語句塊(宣告變數的語句塊)中起作用。

--區域性變數和全域性變數

--全域性變數
if true then
    a = "全域性變數"
end

print(a)

--區域性變數
if true then
    local b = "區域性變數"
end

print(b)

  2.多指令碼執行

    如果想在一個lua指令碼中執行另一個指令碼或者呼叫另一個指令碼中定義的函式等,需要使用require關鍵字引入其他指令碼

--被引用的指令碼
A = "全域性變數A"
local B = "區域性變數B"
--引用其他指令碼

--同資料夾下直接引用指令碼檔名即可,注意:同一個指令碼require引入兩次及以上,並不會執行兩次,也就是說require已經引用的指令碼是無效的
require("BeenRequired")
print(A)
print(B) --B是區域性變數,這個指令碼中看不到

--使用load方法獲取一個boolean值,代表指令碼是否執行過
print(package.loaded["BeenRequired"])

--解除安裝指令碼,直接把loaded的值置為nil即可
package.loaded["BeenRequired"] = nil
print(package.loaded["BeenRequired"])

  3.大G表

    大G表是一個總表,將我們宣告的所有全域性的變數都儲存在裡面(包括大G表自身)。大G表的表名是_G。使用local宣告的本地變數沒有儲存在大G表中。只要執行過的全域性變數都儲存在大G表中,因此即使指令碼被解除安裝了,仍然可以訪問指令碼中宣告過的全域性變數。

    注意:指令碼也可以當成是一個函式,在指令碼最後可以返回外部希望獲取的內容,在外部使用require引入後可以接收這個返回值。

九.lua語法--特殊用法

  1.多變數賦值

    和函式的多返回值類似,多變數賦值會自動補空或者丟棄。

--多變數賦值
a,b,c = 1,2
d,e,f = 3,4,5,6
print(a)
print(b)
print(c)
print(d)
print(e)
print(f)

  2.函式的多返回值:函式部分已經有介紹

  3.邏輯與和邏輯或

    and和or不僅可以連線boolean,任何東西都可以連線,當連線的不是boolean值時,將這些值當成boolean值處理(在lua中,非boolean值當作boolean值處理的規則是nil當作false,其他都當作true處理)。

    注意:lua中不支援三目運算子,但是可以使用and和or模擬三目運算子。

--使用and和or模擬三目運算子的實現
local x = 3
local y = 2
--執行邏輯:利用and和or的短路運算特性
--如果x>y運算的結果為true,則根據and的短路規則會接著判斷x的值是真還是假,顯然x不為nil當作true處理,因此(x>y) and x的結果是true,根據or的短路特性,不會繼續判斷y,因此最後返回x
--如果x>y運算的結果為false,則根據and的短路特性,不會判斷x,(x>y) and x的結果為false,根據or的短路特性,會繼續判斷y,顯然y不為nil當作true處理,因此false or y的最終返回結果為y
local result = (x>y) and x or y
print(result)

十.lua語法--協程

  1.協程的建立

--建立協程

--首先宣告一個函式作為協程函式
function fun()
    print(123)
end

--建立方法一:使用coroutine表中的create函式建立協程
co = coroutine.create(fun)
print(co)
print(type(co))

--建立方法二:使用coroutine表中的wrap函式建立協程
co2 = coroutine.wrap(fun)
print(co2)
print(type(co2))

--兩種方法建立的協程返回值不同,開啟方式也不同
print("*********************")

--通過create函式建立的協程的執行方法
coroutine.resume(co)

--通過wrap方法建立的協程的執行方法
co2()  --wrap方法建立協程返回值為函式型別,直接呼叫函式即可

  2.協程的掛起

--掛起協程

function fun()
    local i = 1
    while true do
        print(i)
        i = i + 1
        --使用coroutine表中的yield函式掛起協程,create函式建立的協程yield的返回值第一個為啟動協程是否成功的boolean值,yield函式傳入的引數是協程這一次掛起的其他返回值,wrap方式建立的協程沒有第一個預設的boolean返回值
        coroutine.yield()
    end
end

co = coroutine.create(fun)
--啟動協程
coroutine.resume(co)  --列印1
--再次啟動協程,繼續執行這個協程
coroutine.resume(co)  --列印2

   3.協程的執行狀態

--協程的狀態

function fun()
    local i = 1
    while i<2 do
        print(i)
        i = i + 1
        print(coroutine.status(co))  --running執行狀態

        --獲取正在執行的協程的執行緒號
        print(coroutine.running())
        coroutine.yield()
    end
end

co = coroutine.create(fun)
--使用coroutine表中的status方法獲取協程的執行狀態
print(coroutine.status(co))  --suspended暫停狀態
coroutine.resume(co)
print(coroutine.status(co))
coroutine.resume(co)
print(coroutine.status(co))  --dead協程結束狀態

十一.lua語法--元表

  1.元表的概念和設定

    1)任何表變數都可以作為另一個表變數的元表

    2)任何表變數都可以由自己的元表(可以理解為父表)

    3)當表進行一些特定操作時,會執行元表中的內容

--元表

meta = {}
myTable = {}

--設定元表
setmetatable(myTable,meta)  --將meta表設定為myTable的元表

  2.特定操作

    1)__tostring

--__tostring

meta = {
    __tostring = function(t)
        return t.name
    end,
}
myTable = {
    name = "movinToString"
}

setmetatable(myTable,meta)

--當子表被當成字串使用時,會呼叫_tostring方法,這個方法可以設定一個引數,系統會預設將子表自身傳入
print(myTable)

    2)__call

--__call

meta = {
    __call = function(c)
        print("movinCall",c.name)
    end
}
myTable = {
    name = "movin"
}

setmetatable(myTable,meta)

--當子表被當作函式使用時,會呼叫元表中的__call函式,同樣的,__call的第一個引數預設為子表自身,不需要傳遞
myTable()

    3)運算子過載

--運算子過載

meta = {
    __add = function(t1,t2)
        return t1.number + t2.number
    end
}
myTable = {
    number = 1
}
myTable2 = {
    number = 2
}

setmetatable(myTable,meta)
setmetatable(myTable2,meta)

--當兩個子表進行運算子操作時,如果這兩個子表有同一個元表且元表中實現了響應的運算子過載,會呼叫這個運算子過載,預設將這兩個子表作為兩個引數傳遞(運算子過載一定是兩個引數)
print(myTable2+myTable)

--其他運算子過載
--__sub(減),__mul(乘),__div(除),__mod(取餘),__pow(冪),__eq(相等),__lt(小於),__le(小於等於),__concat(連線符號..)
--注意:沒有大於和大於等於的運算子過載

    4)__index和__newIndex

--__index和__newindex

meta = {
    age = 2
}
meta.__index = meta
myTable = {}
setmetatable(myTable,meta)

--當呼叫表中沒有的元素時,會自動在元表的__index指向的表中尋找
print(myTable.age)

meta.__newindex = meta
--當設定表中沒有的屬性時,會自動設定屬性到元表的__newindex指向的表中
myTable.name = "movin"
print(myTable.name)

print("****************")

for k,v in pairs(myTable) do
    print(k,v)
end

print("***************")

for k,v in pairs(meta) do
    print(k,v)
end

    注意:為元表中的__index和__newindex賦值時最好在表的外部設定,因為__index在表的內部設定指向元表自身時這個設定是無效的。

  3.元表的一些方法

--元表的相關方法

meta = {}
meta.age = 1
meta.__index = meta
meta.__newindex = meta
mytable = {}
setmetatable(mytable,meta)

--使用getmetatable獲取表的元表
print(getmetatable(mytable))

--使用rawget方法在獲取變數時忽略元表(不去__index對應的表中找,相當於忽略__index這個變數)
print(rawget(mytable,"age"))

--使用rawset方法在設定變數時忽略元表(不去__newindex對應的表中設定,相當於忽略__newindex這個變數)
rawset(mytable,"age",2)
print(mytable.age)
print(meta.age)

十二.lua實現物件導向程式設計

  1.封裝

--封裝

--嘗試封裝萬物之父object
Object = {}
Object.id = 1

--new方法,新建一個物件
function Object:new()
    --宣告一個空表
    local object = {}
    --設定元表
    setmetatable(object,self)
    --元表的__index指向自身,呼叫這個表都會呼叫元表的方法
    self.__index = self
    return object
end

local obj = Object:new()
print(obj)
print(obj.id)

  2.繼承

--繼承

Object = {}

function Object:new()
    local object = {}
    setmetatable(object,self)
    self.__index = self
    return object
end

--提供一個繼承的方法
function Object:subClass(className)
    --使用_G總表,可以建立指定名稱的表
    _G[className] = {}

    --使用元表模擬繼承
    --取出新建立的表
    local obj = _G[className]
    --設定元表
    setmetatable(obj,self)
    self.__index = self
end

--建立新表Person
Object:subClass("Person")

  3.多型

--繼承

Object = {}

function Object:new()
    local object = {}
    setmetatable(object,self)
    self.__index = self
    return object
end

function Object:subClass(className)
    _G[className] = {}
    local obj = _G[className]
    setmetatable(obj,self)
    self.__index = self
    --建立base屬性,實現繼承後子類呼叫父類方法
    self.base = self
end

--Object中提供move方法
function Object:Move()
    print("move")
end

--建立新表Person
Object:subClass("Person")

--重寫父類Object的Move方法
function Person:Move()
    --保留父類邏輯,必須使用點呼叫,不能使用冒號呼叫,然後將呼叫者自身作為引數傳入
    self.base.Move(self)
    print("PersonMove")
end

--建立物件
p = Person:new()

--呼叫Move方法
p:Move()

十三.lua自帶庫

  1.時間相關

--常用自帶庫

--時間相關
--系統時間,毫秒值
print(os.time())
--自己傳入引數得到時間
print(os.time({year = 2021,month = 4,day = 1}))
--得到現在的時刻,將現在的年月日時分秒等儲存在一個表中
local nowtime = os.date("*t")
for k,v in pairs(nowtime) do
    print(k,v)
end

  2.數學運算

--常用自帶庫

--數學運算
--絕對值
print(math.abs(-11))
--弧度轉角度
print(math.deg(math.pi))
--三角函式,引數是弧度值
print(math.sin(math.pi))
--向下或向上取整
print(math.floor(4.3))
print(math.ceil(2.3))
--最值
print(math.max(2,3))
print(math.min(4,2))
--將數字的小數部分和整數部分分離
print(math.modf(3.4))
--冪運算
print(math.pow(3,2))
--隨機數,必須先設定隨機數種子
math.randomseed(os.time())
print(math.random(100))
print(math.random(100))
--開方
print(math.sqrt(121))

  3.路徑

--常用自帶庫

--路徑
--lua指令碼載入路徑
print(package.path)

PS:可以自己檢視_G表中的內容,自帶庫都儲存在_G表中,可以遍歷響應的表格自學。

十四.lua的垃圾回收機制

  lua中存在自動垃圾回收機制,但是在unity中使用lua時儘量手動回收垃圾,減少效能開銷。

--垃圾回收

--得到當前lua佔用記憶體數,單位為千位元組
print(collectgarbage("count"))
--進行垃圾回收的命令
collectgarbage("collect")
--再次檢視lua佔用記憶體數,可以看到佔用變小print(collectgarbage("count"))
print(collectgarbage("count"))

相關文章