一.lua環境安裝
1.SciTE安裝包
Github 下載地址:https://github.com/rjpcomputing/luaforwindows/releases
2.LuaDist(官方推薦,但不是很好用)
http://luadist.org/
二.lua中的注釋
1.單行注釋--
--這里是單行注釋
2.多行注釋
--[[ 這里是多行注釋 ]]--
小技巧:在多行注釋中,--[[添加一個短橫線變成---[[就可以取消多行注釋
三.標示符命名規則
使用大小寫字母或下劃線開頭,加上0個或若干個大小寫字母、數字或下劃線,
注意:lua中內部全域變數基本使用下劃線加上大寫字母命名,因此不推薦這種方式命名標示符,避免沖突,
四.lua中的資料型別
1.資料型別nil
lua中的空值型別,洗掉變數時將變數設定為nil值即可,沒有宣告的變數的值都是nil,
b = 1 print(b) b = nil print(b)

2.資料型別boolean
布林值只有兩個值true和false,表示真和假,值得注意的是,lua中在作邏輯判斷時(如分支結構或回圈結構中的真偽判斷),false和nil都視為假,true和其他值都視為真,
if nil then print(' ') else print(false) end if 1 then print(true) end

3.資料型別number
lua中的數字都是number型別,可以理解為就是c中的double型別,number有如下寫法:
print(2) print(2.2) print(2e3) print(2.34e5) print(3.1e-2)

4.資料型別string
字串型別使用單引號或雙引號參考都可以,也可以使用兩個中括號[[]]來參考字串,
print('我是字串') print("我是字串") print([[我是字串]])

字串的拼接使用..,不能使用+號,
print('lua中的+號會自動將'..'字串兩邊的字串轉化為數字') print('2'..3) print('2'+'3') print(2+3) print('2'+3) print('2+3') print('a'+3)

使用#獲得字串的位元組數,
print(#'aaa aa') print(#'中文一個字符占用兩個位元組')

5.資料型別table
表的構造和基本使用,當定義table時直接定義值,不定義鍵時,table和string一樣,可以使用#獲得table中值的個數(本質上是取得最大索引),
tab1 = {} --空表 {}構造運算式
tab2 = {key1='value1',key2='value2'} --使用鍵值對的形式構造表(和map或dictionary等類似)
tab3 = {'value1','value2','value3'} --直接給出表的值,默認索引為1,2,3,4...,和陣列或list等的性質類似
print(tab1) --直接列印表,列印的是表的地址
print(tab1.key) --表中沒有的鍵對應值為nil
print(tab2.key1) --獲取表中的某個索引對應值的方法一
print(tab2['key1']) --獲取表中某個索引對應值的方法二
print(tab3[2]) --沒有給出索引的表默認索引是1,2,3,4...,不能使用方法一獲取索引對應的值
print(tab3['1']) --索引的型別是number,使用字符1進行取值不能取得值
--使用回圈遍歷表
for k,v in pairs(tab3) do
print(k..':'..v)
end

表的添加值
tab = {} tab.key1 = 'value1' tab.key2 = 'value2' --索引是字串使用.賦值,使用中括號賦值會報錯 tab[10] = 100 --索引是number及其他型別使用中括號進行賦值,使用.賦值同樣報錯,nil不能作為索引, tab[false] = 10 print(tab.key1) --同樣的,在獲取值時也需要注意取值的方式 print(tab[key2]) print(tab[10]) print(tab[false])

6.資料型別function
使用function定義函式,不需要回傳值,引數也不需要定義型別,
function fact(n) if n==1 then return n else return n*(fact(n-1)) end end print(fact(4))

函式同樣可以作為值進行傳遞,
function fact(n) if n==1 then return n else return n*(fact(n-1)) end end print(fact(4)) fact2 = fact print(fact2(5))

函式也可以作為引數傳遞,甚至可以定義匿名函式(類似于委托),
--定義測驗函式 function testFun(tab,fun) for k,v in pairs(tab) do fun(k,v) end end --定義表和引數中的函式 tab = {key1='value1',key2='value2'} function f(k,v) print(k..':'..v) end --呼叫測驗函式 testFun(tab,f)

--定義測驗函式 function testFun(tab,fun) for k,v in pairs(tab) do fun(k,v) end end --定義 tab = {key1='value1',key2='value2'} --呼叫測驗函式,以匿名函式的形式傳遞函式引數 testFun(tab, function (k,v) print(k..' '..v) end )

7.資料型別thread
thread(執行緒)本質上是協程,
8.資料型別userdata
userdata是用戶自定義的資料型別,通常由C/C++創建,
五.語法
1.全域變數和區域變數
lua中的變數不需要宣告型別,默認情況下,變數都是全域變數,可以使用local關鍵字宣告區域變數,區域變數只在當前代碼塊中起作用,一般情況下, 訪問區域變數的速度更快,
function test()
a = 5
local b = 6
end
test()
print(a)
print(b)

2.變數的賦值
變數可以單個賦值,也可以多個同時賦值,賦值的資料型別也可以不同,也可以使用多變數賦值方便地交換變數的值,
--可以單變數賦值,也可以多變數賦值 a = 1 a,b = 1,2 a,b,c = 1,'a',3 print(a,b,c) --交換兩個變數的值 a,b = b,a print(a,b,c) --交換三個變數的值 a,b,c = c,a,b print(a,b,c) --變數的個數多于值,多出來的變數會被自動忽略;值的個數多于變數,多出來的值會被自動忽略 c,d,e = 1,2 f,g = 4,5,6 print(c,d,e,f,g)

3.回圈
while回圈,
i = 1 while i<=5 do print(i) i=i+1 end

for回圈,之前也有用到過遍歷表的for回圈,類似于foreach的使用,
--i從1自增到5,每次自增1 for i=1,5 do print(i) end --i從1自增到10,每次自增2 for i=1,10,2 do print(i) end

repeat until回圈,這個回圈相當于do...while,但是和do...while有所不同,do...while是先執行一次do中的代碼塊,再判斷while的邏輯陳述句,如果滿足條件繼續執行;repeat until是先執行一次until中的代碼塊,再判斷until中的邏輯陳述句,如果不滿足條件繼續執行,
i = 1 repeat print(i) i=i+2 until i>10

4.分支結構
--if陳述句 if 0 then print(0) end --if else陳述句 i = 1 if i<=0 then print(0) else print(1) end --if else if ...陳述句 j = -2 if j <0 then print(-1) elseif j==0 then print(0) else print(1) end

注:回圈結構和分支結構中的,while、for、if等陳述句后面跟上邏輯陳述句,這些邏輯陳述句可以使用括號括起來,也可以使用空格隔開,不使用括號,這里都沒有使用括號,
5.函式
在lua中,函式可以作為資料賦值,也可以作為引數傳遞,
local function max(num1,num2) if num1 < num2 then return num2 else return num1 end end print(max(3,-5)) --函式作為資料賦值 temp = max print(temp(1,8)) myprint = function (param) print('this is my print function '..param) end myprint('ww') --函式作為引數傳遞 function add(num1,num2,printFun) res = num1+num2 printFun(res) end add(40,50,myprint) --lua中的函式可以回傳多個值 function temp() return 10,20,30,40 end res1,res2,res3,res4 = temp() print(res1,res2,res3,res4) --lua中的函式引數個數可變,如print函式 --...代表可變的引數,這些引數會被封裝為一個名稱為arg的表,使用表的方式訪問這些引數,在使用for回圈遍歷表時arg會多一個值表示引數個數 --可變引數前可以有其他引數,但是可變引數一定在引數串列的最后,封裝時只有可變引數部分會被封裝到表arg中 function test(a,...) for k,v in pairs(arg) do print(v) end end test(1,2,3)

六.lua中的運算子
1.算數運算子+、-、*、/、%、^(冪運算子在c#語言中不存在,c#中使用函式求冪)
2.關系運算子==、~=(不等于、 <、>、<=、>=
3.邏輯運算子and、or、not
print(3>2 and 4>3) print(false or false) print(not true)

七.lua中的常見API
1.string有關的API
常見轉義字符:\n 換行、\\ 一個反斜杠、\" 雙引號、\' 單引號
a = 'hello\n\world my name is \'Micheal\'' print(a)

string.upper 將字串轉換為全部大寫 string.lower 將字串轉化為全部小寫
str = 'Hello everybody' str2 = string.upper(str) print(str,str2) str3 = string.lower(str) print(str,str3)

string.gsub 將指定的字符替換為其他指定的字符
str = 'Hello everybody' --將str中的字符e替換為字符8 str2 = string.gsub(str,'e','8') print(str,str2) --將str中的字符e替換為字符123,最多替換1次 str3 = string.gsub(str,'e','123',1) print(str,str3)

string.find 查找指定字符的第一個索引
str = 'Hello everybody' --從頭查找'every'字符的索引(索引從1開始) index = string.find(str,'every') print(index) --從第6個字符開始茶軸'o'字符的索引 index2 = string.find(str,'o',6) print(index2)

string.reverse 反轉字串
str = 'Hello everybody' str2 = string.reverse(str) print(str2)

string.format 字串格式化輸出(未知number使用%d代替,未知string使用%s代替,其他代替格式可以自行查閱)
num1 = 5 num2 = 10 str = string.format('加法:%d+%d=%d',num1,num2,num1+num2) print(str) date,month,year = 30,1,2021 d = string.format('日期:%02d/%02d/%03d',date,month,year) print(d)

string.char 將指定的數字轉化為對應字符 string.byte 將字符轉化為數字(默認轉化字串第一個字符,也可以指定轉化第幾個字符)
print(string.char(97,98,99,100)) print(string.byte('abcd')) print(string.byte('abcd',3))

string.len 獲得指定字串的長度(和#的結果相同)
print(string.len('我有一個夢想'))

string.rep 得到指定字串重復指定次后的字串
print(string.rep('我有一個夢想',5))

string.gmatch 正則運算式匹配,回傳一個迭代器
for word in string.gmatch('Hello lua user','%a+') do print(word) end

2.table有關的API
lua中array本質上是table,因為table是動態的,因此lua中的array也是動態的,值得注意的是,table中的默認索引是從1開始的,因此array中的默認下標也是從1開始的,下面是一個二維陣列的例子:
array = {} for i=1,4 do array[i] = {} for j=1,3 do array[i][j]=i*j end end for i=1,4 do for j=1,3 do print(array[i][j]) end end

陣列的遍歷,
array = {'lua','c#','java'}
--迭代函式一pairs:遍歷table中的key和value
for k,v in pairs(array) do
print(k,v)
end
--迭代函式二ipairs:從索引1開始,遞增遍歷,遇到nil就停止
for k,v in ipairs(array) do
print(k,v)
end
array[2] = nil
for k,v in ipairs(array) do
print(k,v)
end

表可以當作參考型別使用,表賦值給其他表賦值的是地址參考,
table = {'a','b','c'}
newtable = table
print(table[2])
print(newtable[2])
newtable[2] = 'd'
print(table[2])
print(newtable[2])

表的資料拼接,
table1 = {'a','b','c'}
--直接拼接
str = table.concat(table1)
print(str)
--使用,間隔開拼接的資料
str = table.concat(table1,',')
print(str)
--使用,間隔開資料,指定拼接的索引
str = table.concat(table1,',',2,3)
print(str)

表的資料插入和移除,
table1 = {'lua','c#','java','php'}
print(table.concat(table1,','))
--直接插入到末尾
table.insert(table1,'javascript')
print(table.concat(table1,','))
--指定插入到哪一位,后面的資料依次向后移動一位
table.insert(table1,2,'c++')
print(table.concat(table1,','))
--移除表一位資料
table.remove(table1)
print(table.concat(table1,','))
--移除指定位置的資料
table.remove(table1,3)
print(table.concat(table1,','))

表的排序,
table1 = {'lua','c#','java','php','c++','javascript'}
print(table.concat(table1,','))
--排序,按照ASCII碼表順序排列
table.sort(table1)
print(table.concat(table1,','))

八.面向物件編程及其他特性
1.模塊與包
lua中的模塊相當于c#的命名空間或java的包,
--定義模塊,這個模塊保存為檔案Module.luavar = 'movin' func1 = function() print('這是一個函式') end return module
--引入模塊 require 'Module' --使用模塊中的變數 print(var) func1()

lua中的包是指使用C包,lua和C很容易結合,lua就是使用C寫成的,
2.元表
lua提供了元表允許我們改變table的行為,每種行為關聯了對應的元方法,
mytable = {'lua','java','c#','c++'} --普通表
mymetatable = {} --元表 拓展了普通表的行為
mytable = setmetatable(mytable,mymetatable) --關聯元表和普通表
1)__index元方法,這是metatable中最常用的鍵,當通過鍵訪問table的時候,如果這個鍵沒有值,那么lua就會尋找該table的metatable中的__index鍵,如果__index包含一個表格,lua會在表中查找相應的鍵,如果__index包含一個函式,lua就會呼叫那個函式,
mytable = {'lua','java','c#','c++'}
mymetatable = {
__index=function (tab,key)
return 'javascript'
end
}
mytable = setmetatable(mytable,mymetatable)
print(mytable)
print(mymetatable) --表和元表關聯,但是兩個表并不相同,元表是表的拓展
print(mytable[1]) --存在的鍵值
print(mymetatable[1])
print(mytable[10]) --不存在的鍵值,呼叫元表中__index鍵對應的函式
print(mymetatable[10])

2)__newindex元方法,當對表的新索引進行賦值時會起作用,并且會取消賦值操作,
mytable = {'lua','java','c#','c++'}
mymetatable = {
__newindex = function (tab,key,value)
print(string.format('我們要修改的key為%s,value為%s',key,value))
end
}
mytable = setmetatable(mytable,mymetatable)
--下面這些操作都不會觸發__newindex對應的方法
mytable[1] = 'javascript'
print(table.concat(mytable,' '))
table.insert(mytable,'lua')
print(table.concat(mytable,' '))
table.insert(mytable,3,'c')
print(table.concat(mytable,' '))
table.remove(mytable,1)
print(table.concat(mytable,' '))
table.remove(mytable)
print(table.concat(mytable,' '))
mytable[5] = 'lua' --對新索引進行賦值時會呼叫__newtable對應的方法,而且不會進行賦值操作
print(table.concat(mytable,' '))

mytable = {'lua','java','c#','c++'}
mymetatable = {
__newindex = function (tab,key,value)
rawset(tab,key,value) --由于呼叫了__newindex對應的函式后不會進行賦值,若想賦值,可以使用rawset函式(如果直接賦值會產生死回圈)
--mytable[5] = 'lua' 這里采用這種方式賦值會產生死回圈
end
}
mytable = setmetatable(mytable,mymetatable)
mytable[5] = 'lua'
print(table.concat(mytable,' '))

mytable = {'lua','java','c#','c++'}
newtable = {}
mymetatable = {
__newindex = newtable --__newindex對應的是一個表
}
mytable = setmetatable(mytable,mymetatable)
mytable[5] = 'javascript' --設定不存在的索引的值時會設定到__newindex對應的表中
print(mytable[5])
print(newtable[5]) --在__newindex對應的表中,索引值還是5

3)運算子元方法,
mytable = {'lua','java','c#','c++'}
mymetatable = {
__add = function(tab,newtab) --__add索引的值定義了表的加法操作,這里將第二個相加的表的所有鍵值對拼接到第一個表的最后
for k,v in pairs(newtab) do
table.insert(tab,v)
end
return tab
end
}
mytable = setmetatable(mytable,mymetatable)
newtable = {'python','php'}
print(table.concat(mytable+newtable,' '))

除了__add外,對應其他運算的元方法有:__sub(減法)__mul(乘法)__div(除法)__mod(求模)__pow(乘方)__unm(取負)__concat(連接)__eq(是否相等)__lt(小于)__le(小于等于)
4)__call元方法
將表當作函式呼叫時呼叫__call對應的函式,
mytable = {'lua','java','c#','c++'}
mymetatable = {
__call = function(tab,arg)
print(arg)
end
}
mytable = setmetatable(mytable,mymetatable)
mytable('我是大帥比')

5)__tostring元方法
將表當作字串使用(如print(table))時定義表對應的字串,
mytable = {'lua','java','c#','c++'}
mymetatable = {
__tostring = function(tab)
str = ''
for k,v in pairs(tab) do
str = str..k..' '..v..','
end
return str
end
}
mytable = setmetatable(mytable,mymetatable)
print(mytable)

3.協程
定義和啟動協程,
方式一:
--定義協同函式 co = coroutine.create( function (a,b) --必須是匿名函式 print(a+b) end ) --運行協同函式 coroutine.resume(co,20,40)

方式二:
--定義協同函式 co = coroutine.wrap( function (a,b) --必須是匿名函式 print(a+b) end ) --運行協同函式 co(20,40)

協程的掛起和繼續運行,
--定義協同函式 co = coroutine.create( function (a,b) print(a+b) coroutine.yield() --掛起協同函式 print(a-b) end ) --運行協同函式 coroutine.resume(co,20,40) --這句代碼的位置在運行協同函式的代碼后,在重啟協同函式的代碼前,因此掛起協同函式后會運行這段代碼 print("I'm here") --繼續運行協同函式 coroutine.resume(co)

協同程式的回傳值,
co = coroutine.create( function (a,b) return a+b,a-b end ) --協同函式默認有一個boolean型別的回傳值表示是否啟動成功,自己定義的其他回傳值作為第二個、第三個......等回傳值 res1,res2,res3 = coroutine.resume(co,20,40) print(res1,res2,res3)

co = coroutine.create( function (a,b) print('........................') coroutine.yield(a+b,a-b) print('........................') return a*b,a/b end ) --當協同函式中間被暫停時,可以分階段回傳不同的回傳值,使當前階段暫停的yield函式括號中的引數作為當前階段的回傳值 res1,res2,res3 = coroutine.resume(co,20,40) print(res1,res2,res3) --最后一個運行階段的回傳值為return回傳的內容 res4,res5,res6 = coroutine.resume(co) print(res4,res5,res6)

協程的狀態(3種),
co = coroutine.create( function (a,b) print(coroutine.status(co)) --running運行 print('........................') coroutine.yield() print('........................') print(coroutine.status(co)) --running運行 end ) print(coroutine.status(co)) --suspended暫停 coroutine.resume(co,20,40) print(coroutine.status(co)) --suspended暫停 coroutine.resume(co) print(coroutine.status(co)) --dead死亡

獲取正在運行的協程,
co = coroutine.create( function (a,b) print(coroutine.running()) --獲取正在運行的協程 end ) coroutine.resume(co,20,40)

4.檔案I/O
檔案的簡單讀取和寫入,
f = io.open('iotest.txt','r') --打開檔案 io.input(f) --創建輸入流 print(io.read()) --read函式讀取一行 print(io.read()) print(io.read()) print(io.read()) io.close() --關閉流

open函式的第一個引數是檔案的相對地址和名稱,第二個引數是可選引數,對應打開方式,打開方式有:r(只讀,檔案必須存在)、w(只寫,寫入的檔案原有資料會被清空,檔案不存在會自動創建檔案)、a(以附加的方式打開只寫檔案,若檔案不存在,則會建立該檔案,如果檔案存在,寫入的資料會被加到檔案尾,即檔案原先的內容會被保留)、r+(以可讀寫方式打開檔案,該檔案必須存在)、w+(打開可讀寫檔案,若檔案存在則檔案長度清為零,即該檔案內容會消失,若檔案不存在則建立該檔案)、a+(與a類似,但此檔案可讀可寫)、b(二進制模式,如果檔案是二進制檔案,可以加上b)
f = io.open('iotest.txt','a') --打開檔案,a為追加的形式只寫,w為清空后只寫 io.output(f) --創建輸出流 print(io.write('')) --write函式寫入內容,回傳值代表是否寫入成功 io.close() --關閉流

read函式的引數,引數有:"*n"(讀取一個數字并回傳)、"*a"(從當前位置讀取整個檔案)、"*l"(默認引數,讀取下一行)、number(回傳指定個數的字串),
f = io.open('iotest.txt','r') io.input(f) print(io.read("*l")) --讀取一行 print(io.read("*n")) --讀取一個數字 print(io.read(10)) --讀取10個字符 print(io.read("*a")) --讀取剩下的所有內容 io.close()

完全模式,完全模式下,可以同時處理多個檔案,
f = io.open('iotest.txt','r') --使用f:read代替io.read print(f:read()) file.close()

5.lua實作面向物件編程
lua中并沒有直接實作面向物件編程,但是可以使用表間接實作面向物件,
--定義一個人的物件 person = {name='movin',age=18} person.eat = function () print(person.name..'在吃飯') end person.eat()

優化面向物件的實作,
--定義一個人的物件 person = {name='movin',age=18} --將物件自身作為變數傳遞,否則這里物件的名稱不能修改 person.eat = function (self) print(self.name..'在吃飯') end person.eat(person)

--定義一個人的物件 person = {name='movin',age=18} --使用冒號定義和呼叫,不用傳遞self引數,其中self就指代呼叫者 function person:eat() print(self.name..'在吃飯') end person:eat()

根據模板創建新物件,
Person = {name='movin',age=18}
function Person:eat()
print(self.name..'在吃飯')
end
--創建新的物件的new方法
function Person:new()
local t = {}
--使用元表的__index元方法指向Person物件
setmetatable(t,{__index=self})
return t
end
person1 = Person:new()
person2 = Person:new()
print(person1.name)
person2:eat()
--修改物件中的屬性值相當于在物件中設定了新值,再查找這個索引對應的值時就不會在Person中查找,相當于實作了修改屬性和重寫方法的效果
person1.name = 'ww'
print(person1.name)

__index元方法相當于實作了繼承,元表相當于父表,當子表中沒有某個屬性或方法時,從父表中查找;當子表中重新賦值了某些屬性或重寫了某些方法時,就直接從子表中呼叫,
轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/255506.html
標籤:其他
上一篇:2021寒假每日一題《K倍區間》
