序言:一直專一C#開發,Lua算是做爲第二語言吧,須要全面的基本的瞭解一下。下述案例本身跟着敲了一遍,還有補充一些本身感到疑惑的。介紹了lua 重點部分。html
Lua是一種輕量小巧的腳本語言,用標準C語言編寫並以源代碼形式開放。數據庫
設計目的:嵌入應用程序中,從而爲應用程序提供靈活的擴展和定製功能。編程
特性:數組
①輕量級,用標準C語言編寫並以源代碼形式開放,編譯後僅僅一百餘K,能夠很方便的嵌入到別的程序裏。安全
②可擴展性,Lua提供了很是易於使用的擴展和機制:由宿主語言(C/C++)提供的這些功能,Lua可使用它們,就像原本就內置的功能同樣。數據結構
其餘特性:多線程
①支持面向過程和函數式編程。閉包
②自動內存管理:只提供了一種通用類型的表(table),用它能夠實現數組,哈希表,集合,對象;socket
③語言內置模式匹配;閉包;函數也能夠看作一個值;提供多線程(協同程序,並不是操做系統所支持的線程)支持。ide
④經過閉包和table能夠很方便的支持面向對象編程所須要的一些關鍵機制,好比數據抽象,虛函數,繼承和重載等。
Lua應用場景:
遊戲開發
獨立應用腳本
Web應用腳本
擴展和數據庫插件
安全系統,如入侵檢測系統
連接:https://pan.baidu.com/s/1S3rHjW6YOMJbPgGvMVjHlA
提取碼:owiv
下載這個上述資源,以後根據這個步驟:https://blog.csdn.net/wue1206/article/details/81463503,親測可用。
Lua是動態類型語言,變量不要類型定義,只須要爲變量賦值。值能夠存儲在變量中,做爲參數傳遞或結果返回。
nil,表示無效值。
I.表示刪除一個值。
II.做比較的時候,應該加上雙引號,type(X)=="nil",原理能夠看以下案例。
III.nil能夠看成boolean中false.
boolean,
boolean類型只有兩個可選值:true(真)false(假),Lua把false和nil看做「假」,其餘爲「真」。
number,雙精度類型的實浮點數
string,由一對雙引號或單引號,或者[[]].
①在對一個數字字符串進行算術操做時,Lua會嘗試將這個數字字符串轉成一個數字
print("2"+"3") -- 5.0
print("2+3") -- 2+3
②#計算字符串長度,放在字符串前面
#"shshshd" --7
function,由C或Lua編寫的函數
①函數式被看做是「第一類值」,函數能夠存在變量裏
userdata,表示任意存儲在變量中的C數據結構
一種用戶自定義數據,用於表示一種由應用程序或C/C++語言庫所建立的類型,能夠將任意C/C++的任意數據類型的數據(一般是struct和指針)存儲到Lua變量中調用。
thread,在lua裏最主要的線程是協同程序(coroutine)。他跟線程差很少,擁有本身獨立的棧,局部變量和指令指針,能夠跟其餘協同程序共享全局變量和其餘大部分東西。
線程和協程的區別:線程能夠同時多個運行,而協程任意時刻只能運行一個,而且處於運行狀態的協程只有被掛起(suspend)時,纔會暫停。
table,
①Lua中的表(table),實際上是一個「關聯數組」,數組的索引可使數字,字符串。
②在Lua裏,table的建立是經過「構造表達式」來完成。最簡單的單構造表達式是{},用來建立一個空表。
③不一樣於其餘語言的數組,Lua默認初始索引是1開始。
④table長度不會固定大小,有新數據添加table長度會自動增加,沒初始化的table都是nil
Lua中的變量全是局部變量,哪怕是語句塊或函數中,除非用local顯式聲明爲局部變量。局部變量的做用域爲從聲明位置開始到所在語句塊結束。
變量的默認值均爲nil。
賦值語句
Lua能夠多個變量同時賦值,變量列表和值列表的各個元素用逗號隔開,賦值語句右邊的值會依次賦值給左邊的變量。多值賦值常常用來交換,或將函數調用返回給變量
遇到賦值語句Lua會先計算右邊的值,而後再執行賦值操做,因此咱們能夠這樣交換變量的值。如下是2個值和2個值賦值
當變量個數和值不一致時,不足則補nil,多餘則忽略。
索引
對table的索引使用方括號[]。Lua也提供了 . 點操做符。
t[i] 或 t.i
①while循環
while(condition) do statements end
②for循環
I.for do
for i =1,10 do print(i) end
II.for k,v in pairs(tab) do
適用於對象
III.for k,v in ipairs(tab) do
相對上述方案,限制較多:
k 只能是整型,且必須順序+1遞增,不然以後的再也不遍歷。
若是v 爲nil ,則也中止執行。
適用於數組
local ret = {} ret[1] = nil ret[2] = "nil" for k,v in ipairs(ret) do print(k.."|"..v) end
③repeat...until
重複執行,直到指定的條件爲真(與while循環相反,不知道這個存在的意義是啥)
local i = 0; repeat i=i+1 print(i) until(i>5)
local i = 0 if(i==0) then print("if data") elseif(i==1) then print("elseif data") elseif(i==2) then print("elseif data") else print("else data") end
①Lua編程中,常常遇到函數的定義好調用。咱們有兩種函數定義和調用的方法。一種是用屬性的定義,另一種是經過冒號的形式(其實也是屬性)。只不過用冒號形式聲明的函數默認有一個參數self。self指向調用者自己。
單個點的調用。
改爲下述寫法就對了
Animal ={id = 2,name = "djw"} function Animal:PrintLog() print(self.id) end Animal.PrintLog(Animal) Animal:PrintLog()--寫冒號,表明默認第一個參數傳遞自己
還能夠這麼寫
Animal ={id = 2,name = "djw"} function Animal.PrintLog(tab) print(tab.id) end Animal:PrintLog()--寫冒號,表明默認第一個參數傳遞自己
②在調用函數時,也須要將對應的參數放在一對圓括號裏,即便調用函數時沒有參數,也必須寫一對空括號。對於這個規則只有一種特殊的例外:一個函數若只有一個參數,而且此參數是一個字符串或table的構造是,那麼圓括號即可以省略掉。
print "hellow" print[[helllllll]] function f(tab) for k,v in pairs(tab) do print(k.."|"..v) end end f({x=1,y=2}) f{x=3,y=10}
function foo0() end -- 無返回值 function foo1() return "a" end -- 返回一個結果 function foo2() return "a", "b" end -- 返回兩個結果 -- 在多重賦值時,若是一個函數調用是最後,或僅有的一個表達式, -- 那麼Lua會保留其儘量多的返回值,用於匹配賦值變量 x, y = foo2() -- x = "a", y = "b" x = foo2() -- x = "a", "b"被丟棄 x, y, z = 10, foo2() -- x = 10, y = "a", z = "b" -- 若是一個函數沒有返回值或者沒有足夠多的返回值,那麼Lua會用 -- nil來補充缺失的值 x, y = foo0() -- x = nil, y = nil x, y = foo1() -- x = "a", y = nil x, y, z = foo2() -- x = "a", y = "b", z = nil -- 若是一個函數調用不是一系列表達式的最後一個元素,那麼將只產生一個值: !!!!!!!!!! x, y = foo2(), 20 -- x = "a", y = 20 x, y = foo0(), 20, 30 -- x = nil, y = 20, 30則被丟棄 -- table構造式能夠完整的接收一個函數調用的全部結果,即不會有任何數量 -- 方面的調整 local t = {foo0()} -- t = {}(一個空的table) local t = {foo1()} -- t = {"a"} local t = {foo2()} -- t = {"a", "b"} -- 可是,對於上述的行爲,只有當一個函數調用做爲最後一個元素時纔會發生, -- 而在其餘位置上的函數調用老是隻產生一個結果值 local t = {foo0(), foo2(), 4} -- t[1] = nil, t[2] = "a", t[3] = 4 -- 咱們也能夠在一個函數中,使用return返回另外一個函數的返回值 function MyFunc() -- 返回a return foo1() -- 注:這裏是return foo1(),而不是return (foo1()) end -- return foo1()和return (foo1())是兩個徹底不一樣的意思 -- 將一個函數調用放入一對圓括號中,從而迫使它只返回一個結果!!!!!!!!!!!! print((foo0())) -- nil print((foo1())) -- a print((foo2())) -- a
①多返回值,能夠返回多個結果值
function max(num1,num2) if(num1>num2) then return num1 end return num2,num1,num2 end print(max(10,2))
②可變參數,能夠接受可變數目的參數,和C語言相似,在函數列表中使用三個點 ... 表示函數有可變的參數。注意{...}表明變長參數構成的數組
function test(...) print("可變參數長度爲:"..select("#",...)) for k,v in pairs({...}) do print(k..v) end end test(1,3,4,"ss")
能夠經過select("#",...) 獲取可變參數的數量
還能夠經過select(n,...),用於返回n到select("#",...)的參數
迭代器(iterator)是一種對象,他可以用來遍歷標準模板庫容器中的部分或所有元素,每一個迭代器對象表明容器中的肯定的地址。
在Lua中迭代器是一種支持指針類型的結構,他能夠遍歷集合的每個元素。
泛型for迭代器,泛型for在本身內部保存迭代函數,實際上它保存三個值:迭代函數,狀態常量,控制變量。
local array = {"aaaa","bbbb"} for k,v in ipairs(array) do print(k..v) end
泛型for執行過程
①初始化,計算in後面的表達式的值,表達式應該返回泛型for須要的三個值:迭代函數,狀態常量,控制變量;與多值賦值同樣,若是表達式返回的結果個數不足三個會自動用nil補足,多出部分會忽略。
②將狀態常量和控制變量做爲參數調用迭代函數(注意:對於for結構來講,狀態常量沒有用處,僅僅在初始化時獲取他的值並傳遞給迭代函數)
③將迭代函數返回的值賦給變量列表
④若是返回的第一個值爲nil循環結束,不然執行循環體
⑤回到第二步調用迭代函數
分兩種迭代器
無狀態的迭代器,指的是不保留任何狀態的迭代器,所以咱們在循環中能夠利用無狀態迭代器避免建立閉包花費的額外的代價。每一次迭代,迭代函數都是用兩個變量(狀態常量和控制變量)的值做爲參數被調用。一個無狀態的迭代器只利用這兩個值能夠獲取下一個元素。這種無狀態的迭代器的典型的簡單例子就是ipairs,它遍歷數組的每個元素。迭代的狀態包括被遍歷的表(循環過程當中不會改變的狀態常量)和當前的索引下標,ipairs和迭代函數都很簡單,咱們在Lua中能夠這麼實現:
多狀態的迭代器,不少狀況下,迭代器須要保存多個狀態信息而不是簡單的狀態常量和控制變量,最簡單的方法就是使用閉包。還有一種方法就是將全部的狀態信息封裝到table內,將table做爲迭代器的狀態常量,由於這種狀況下能夠將全部的信息存放在table內,因此迭代函數一般不須要第二個參數了。
function cpairs(arr) local index = 0 local count =#arr return function () index =index +1 if(index <=count) then return arr[index] end end end local arr = {"djw","dll"} for v in cpairs(arr) do print(v) end
table是Lua的一種數據結構用來幫助咱們建立不一樣的數據類型,如數組,字典等。
Lua table使用關聯型數組,你能夠用任意類型的值來做數組的索引,但這個值不能是nil。
Lua table是不固定大小的,你能夠根據本身須要來進行擴容。
Lua也是經過table來解決模塊(module),包(package)和對象(Object)的。
table的構造
構造器是建立和初始化表的表達式。表示Lua特有的功能強大的東西。最簡單的是構造函數{},用來建立一個空表。直接能夠初始化數組。
mytable ={}
mytable[2]="lua"
mytable = nil -- 移除引用,垃圾回收會釋放內存
當咱們爲table a並設置元素,而後將a賦值給b,則a與b都指向同一個內存。若是a設置爲nil,則b一樣能訪問table的元素。若是沒有指定的變量指向a,lua的垃圾回收機制會清理相對應的內存。
table的操做
table.concat(table,sep,start,end),concat是concatenate(連鎖,連接)的縮寫,table.concat()函數列出函數中指定table的數組部分從start位置到end位置的全部元素,元素間以指定的分隔符sep隔開
table.insert(table,pos,value),在table的數組部分指定位置pos插入值爲value的一個元素,pos參數可選,默認爲數組部分末尾
table.remove,返回table數組部分位於pos位置的元素,其後的元素會被前移,pos參數可選,默認爲table的長度,即從最後一個元素刪起。
table.sort,對給定的數組進行正序排序
注意:當咱們在獲取table的長度的時候,不管是使用#仍是table.getn,其都會在索引中斷的地方中止計數,而致使沒法正確取得table的長度。
可使用下述方法來代替:
function getlength(tab) int index = 0 for k,v in pairs(tab) do index = index +1 end return index end
模塊相似於一個封裝庫,Lua加入了標準的模塊管理機制,能夠把一些公用的代碼放在一個文件裏,以API的接口的形式在其餘地方調用,有利於代碼的重用和下降代碼耦合度。
Lua的模塊是由變量函數等已知元素組成的table,所以建立一個模塊很簡單,就是建立一個table,而後把須要導出的常量,函數放入其中,最後返回這個table就行。
module = {} module.costant = "常量" function module.func1() print("func1") end function module.func2() print("func2") end local function func3() print("local func3") end return module
由上可知,模塊的機構就是一個table的結構,所以能夠像操做table裏的元素那樣來操做調用模塊裏的常量或函數。
require函數
Lua提供了一個名爲require的函數用來加載模塊。要加載一個模塊,只須要簡單的調用就能夠了。
require("<模塊名>") require"<模塊名>"
執行require後會返回一個由模塊常量或函數組成的table,而且還會定義一個包含該table的全局變量。
注意:require的文件所在路徑
加載機制(這段沒明白)
因爲自定義的模塊,模塊文件不是放在哪一個文件目錄都行,函數require有它本身的文件路勁加載策略,他會嘗試從Lua文件或C程序庫中加載模塊。
require用於搜索Lua文件的路勁是存放在全局變量package.path中,當Lua啓動後,會以環境變量LUA_PATH的值來初始化這個環境變量。若是沒有找到該環境變量,則使用一個編譯時定義的默認路徑來初始化。
若是找到目標文件,則會調用package.loadfile來加載模塊。不然,就會去C程序庫找。
搜索的文件路徑是從全局變量package.cpath獲取,而這個變量則是經過環境變量LUA_CPATH來初始。
搜索的策略跟上面同樣,只不過如今換成搜索的是so或dll類型的文件。若是找到,那麼require就會經過package.loadlib來加載它。
C包(這段沒明白)
Lua和C是很容易結合的,shiyongC爲Lua寫包。
與Lua中寫包不一樣,C包在使用之前必須先加載並鏈接,在大多數系統中最容易得實現方式是經過動態連接庫機制。
Lua在一個叫loadlib的函數內提供了全部的動態鏈接的功能。這個函數有兩個參數,庫的絕對路徑和初始化函數。
local path = "/user/local/lua/lib/libluasocket.so" local f = loadlib(path,"luaopen_socket")
loadlib函數加載指定的庫而且鏈接到Lua,然而它並不打開庫(也就是說沒有調用初始化函數),反而他返回初始化函數做爲Lua的一個函數,這樣咱們就能夠直接在Lua中調用它。
若是動態加載庫或者查找初始化函數時出錯,loadlib將返回nil和錯誤信息。咱們能夠修改前面一段代碼,使其檢測錯誤而後調用初始化函數:
local path = "/user/local/lua/lib/libluasocket.so" local f = assert(loadlib(path,"luaopen_socket")) f() --真正打開庫
在Lua table中咱們能夠訪問對應的key來獲得value值,可是卻沒法對兩個table在進行操做。
爲此,lua提供了元表(Metatable),容許咱們改變table的行爲,每一個行爲關聯了對應的元方法。
①__add,lua計算兩個table的相加操做
當lua試圖對兩個表進行相加時,先檢查二者之一知否有元表,以後檢查是否有一個叫"__add"的字段,若找到,則調用對應的值。"__add"即字段,其對應的值(每每是一個函數或是table)就是"元方法"。
有兩個重要的函數處理元表:
setmetatable(table,metatable):對指定table設置元表(metatable),若是元表(metatable)中存在__metatable健值,setmetatable會失敗。
getmetatable(table):返回對象的元素(metatable)
ret = {} meta = {} setmetatable(ret,meta) print(getmetatable(ret)) --ret2 = setmetatable({},{}) 另一種寫法
②__index
這是metatable最經常使用的健。
當你經過健來訪問table的時候,若是這個健沒有值,那麼lua就會尋找該table的metatable(假設有metatable)中的__index健。若是__index包含一個表格,Lua會在表格中查找相應的健。
若是__index包含一個函數的話,lua就會調用那個函數,table和健會做爲參數傳遞給函數。__index元方法查看錶中元素是否存在,若是不存在,返回結果爲nil;若是存在,則由__index返回結果。
lua查找一個表元素時的規則:
I.在表中查找,若是找到,返回該元素,找不到則繼續
II.判斷該表中是否有元表,若是沒有元表,則返回nil,有元表則繼續
III.判斷元表有沒有__index方法,若是__index方法爲nil,則返回nil;若是__idex 方法是一個表,則重複I,II,III;若是__index方法是一個函數,則返回該函數的返回值。
③__newindex
用來對錶更新。
當你給表的一個缺乏的索引賦值,解釋器就會查找__newindex元方法;若是存在則調用這個函數而不進行賦值操做。
以上實例中表設置了元方法__newindex,在對新索引(newKey)賦值時,會調用元方法,而不進行賦值。而若是對已存在的索引健(key1),則會進行賦值,而不調用元方法__newindex
④爲表添加操做符
⑤__call元方法
__call元方法在lua調用一個值時調用。
⑥__tostring 元方法
__tostring用於修改表的輸出行爲。
lua協同程序(coroutine)與線程相似:擁有獨立的堆棧,獨立的局部變量,獨立的指令指針,同時又與其餘協同程序共享全局變量和其餘大部分東西。
協同和線程的區別:一個具備多個線程的程序能夠同時運行幾個線程,而協同程序卻須要彼此協做的運行。在任意時刻只有一個協同程序在運行,而且這個正在運行的協同程序只有在明確的要求被掛起的時候纔會被掛起。協同程序有點相似同步的多線程,在等待同一個線程鎖的幾個線程有點相似協同。
coroutine.running就能夠看出來,coroutine在底層實現的就是一個線程。
當create一個coroutine的時候就是在新線程中註冊了一個事件。
當使用resume觸發事件的時候,create的coroutine函數就被執行了,當遇到yield的時候就表明掛起當前線程,等候再次resume觸發事件。
生產者-消費者問題
local newProductor function productor() local i = 0 while true do i = i+1 send(i) end end function consumer() while(true) do local i =receive() print(i) end end function receive() local status,value = coroutine.resume(newProductor) return value end function send(x) coroutine.yield(x) end newProductor = coroutine.create(productor) consumer()
lua採用了自動內存管理。意味着你不用操心新建立的對象須要的內存是如何分配出來,也不用考慮在對象再也不被使用後怎麼樣釋放所佔用的內存。
lua運行了一個垃圾收集器來收集全部死對象(即Lua不可能訪問到的對象)來完成自動管理內存管理的工做。lua中所用到的內存:字符串,表,函數,線程等都服從內部管理。
lua實現了一個增量標記-掃描器。它使用兩個數字來控制垃圾收集循環:(都使用百分數爲單位,100在內部表示1)
①間歇率:控制着垃圾收集器須要開啓新的循環前要等待多久。增大這個值會減小收集器的積極性。當這個值比100小的時候,收集器在新的循環前不會有等待。設置這個值爲200就會讓收集器等到總內存使用量達到以前的兩倍時纔開始新的循環。
②步進倍率:控制着收集器運做速度相對於內存分配速度的倍率。增大這個值不只會讓收集器更加積極,還會增長每一個增量步驟的長度。不要把這個值設置小於100,那樣的話,收集器工做太慢了以致於永遠都幹不完一個循環。默認值是200,這表示收集器之內存分配的「兩倍」速度工做。
十四.面向對象
I.封裝,指可以把一個實體的信息,功能,響應都裝入一個單獨的對象中的特性。
II.繼承,繼承的方法容許在不改動原程序的基礎上對其進行擴充,這樣使得原功能得以保存,而新功能也得以擴展。這有利於減小重複編碼,提升軟件的開發效率。
III.多態,同一操做做用於不一樣的對象,能夠有不一樣的解釋,產生不一樣的執行結果。
IV.抽象,抽象是簡化複雜的現實問題的途徑,它能夠爲具體問題找到最恰當的類定義,而且能夠在最恰當的集成級別解釋問題。
咱們知道對象由屬性和方法組成。lua中最基本的結構是table,因此須要用table來描述對象的屬性。
lua中的function能夠用來表示方法。那麼lua中的類能夠經過table+function模擬出來。至於繼承,能夠經過metetable模擬出來。
lua中的表不只在某種意義上是一種對象。像對象同樣,表也有狀態(成員變量);也有與對象的值獨立的本性,特別是擁有兩個不一樣值得對象(table)表明兩個不一樣的對象;一個對象在不一樣的時候也能夠有兩個不一樣的值,但他始終是一個對象;與對象相似,表的生命週期與其由什麼建立,在哪建立沒有關係。對象有他們的成員函數,表也有。
①建立對象,主要使用__index元表實現
people = {id = 0,name = ""} function people:new(i,n) local o = {} setmetatable(o,self) self.__index = self self.id = i self.name = n return o end function people:PrintSelf() print("id:"..self.id.."name:"..self.name) end local djw = people:new(1000,"djw") djw:PrintSelf() djw.PrintSelf(djw)--同上一行一致,寫法不一樣而已,參考函數章節 local xiaohong = people:new(1001,"xiaohong") xiaohong:PrintSelf() xiaohong.PrintSelf(xiaohong)
②繼承,指一個對象直接使用另一個對象的屬性和方法。可用於擴展基礎類的屬性和方法。
WoMan =people:new() function WoMan:new(i,n) o = people:new(i,n) setmetatable(o,self) self.__index = self return o end function WoMan:TestOne() print(self.id.."|||"..self.name) end local xiaohong = WoMan:new(10,"xiaohong") xiaohong:TestOne() xiaohong:PrintSelf()
參考:https://www.runoob.com/lua/lua-tutorial.html
https://www.cnblogs.com/ring1992/p/6000731.html
https://www.cnblogs.com/ring1992/p/6000893.html