lua是的小巧的腳本語言,它的設計目的是爲了可以嵌入到應用程序中,從而爲應用程序提供靈活的擴展和定製功能。javascript
先執行百度安裝好 lua,本文是採用 vscode 運行的,windows 用戶可使用lua 自帶的編輯器。php
-- 這是單行註釋 --[[ 這裏是多行註釋 註釋 --]]
ua 是動態類型語言,變量不要類型定義,只須要爲變量賦值。 值能夠存儲在變量中,做爲參數傳遞或結果返回。java
Lua 中有 8 個基本類型分別爲:nil、boolean、number、string、userdata、function、thread 和 table。python
數據類型 | 描述 |
---|---|
nil | 這個最簡單,只有值nil屬於該類,表示一個無效值(在條件表達式中至關於false)。 |
boolean | 包含兩個值:false和true。 |
number | 表示雙精度類型的實浮點數 |
string | 字符串由一對雙引號或單引號來表示 |
function | 由 C 或 Lua 編寫的函數 |
userdata | 表示任意存儲在變量中的C數據結構 |
thread | 表示執行的獨立線路,用於執行協同程序 |
table | Lua 中的表(table)實際上是一個"關聯數組"(associative arrays),數組的索引能夠是數字、字符串或表類型。在 Lua 裏,table 的建立是經過"構造表達式"來完成,最簡單構造表達式是{},用來建立一個空表。 |
操做符 | 描述 | 實例 |
---|---|---|
+ | 加法 | A + B 輸出結果 30 |
- | 減法 | A - B 輸出結果 -10 |
* | 乘法 | A * B 輸出結果 200 |
/ | 除法 | B / A w輸出結果 2 |
% | 取餘 | B % A 輸出結果 0 |
^ | 乘冪 | A^2 輸出結果 100 |
- | 負號 | -A 輸出結果 -10 |
bool = nil -- 這個最簡單,只有值nil屬於該類,表示一個無效值(在條件表達式中至關於false)。 id = 1 -- 聲明整數 amount = 1.2 -- 聲明整數(lua 整數和小數都叫作整數) name = 'jacky' -- 聲明字符串,單引號雙引號一個意思,都是字符串 nickName = "哈基石" -- 聲明字符串 local age = 18 -- 聲明局部變量,帶有 local 的是局部定義,不帶有的是全局定義 print(id..name) -- 鏈接兩個變量或字符相似 php 中的 . 和 javascript 中的 +
主要是先熟悉一下 Lua 的語法結構,你不用記後面會大部分用到,有一點印象便可。golang
bool = true -- 定義全局變量 if bool == true then print(true) -- 輸出 true end if 0 then print(true) -- 輸出 true end if nil then print(true) -- 不輸出 重點記憶 end if bool == true then print(true) -- 輸出 true else print(false) end local num = 3 if num == 1 then print(1) elseif num == 2 then print(2) else print(3) -- 輸出3 end
其實 table 應該放後面學的,但總以爲放後面會致使知識很零散,沒有辦法高效學習。學會了 table,本文基本上60%就已經學完了。sql
相對於其餘語言而言,lua 不須要學習那麼多結構像數組,結構體,map,切片之類的。都說 php 的數組一招鮮走遍天下,啥都能幹。windows
可是 lua更恐怖,有着更強大的功能呢,包括數組,類,匿名...數組
lua 的數字鍵值是從1開始的,這一點須要特別注意。數據結構
數字做爲 key 的數組閉包
--建立一個空的 table tab = {} -- 直接初始化 table tab = {"java", "golang", "lua", "python"} -- 數字下標訪問 print(tab[1]) -- 輸出 java print(tab[2]) -- 輸出 golangtabl tab[1] = "C#" -- 賦值 tab[7] = "C++" -- 賦值 print(tab[1]) -- 輸出 C# print(tab[7]) -- 輸出 C++ tab[1] = nil -- 刪除數組中的元素, key 保留值爲 nil print(tab[1]) -- 輸出 nil print(tab[1000]) -- 輸出 nil 不會報錯哦
字符串做爲 key 的數組
tab = {beijing="北京", shanghai="上海", chengdu="成都"} tab["hangzhou"] = "杭州" print(tab.beijing, tab.hangzhou, tab["shanghai"]) -- 輸出 北京 杭州 上海
總結: 若是是數字做爲索引,那麼須要用[]
來訪問,若是字符串做爲索引,那麼可使用[], 和 xx.xxx
兩種方式均可以訪問和修改
table的增長
-- 新增元素到 tab 的末尾 tab = {"beijing", "shanghai"} table.insert(tab, "hangzhou") -- 將廣州寫入tab的末尾,下標就是3 print(tab[3]) -- hangzhou -- 新增到指定的位置 table.insert(tab, 1, "neimeng") -- 插入到1的位置,其原來位置後面的元素向後偏移 print(tab[1], tab[2]) -- 輸出 neimeng beijing
table 的刪除
tab = {"java", "C++", "golang", "sql"} res = table.remove(tab, 2) -- 刪除第二個元素,C++,其餘元素會進行補位(向前偏移) print(res, tab[1], tab[2]) -- 輸出C++ java golang
多維 table 與一位多了一層,使用狀況徹底同樣。
tab2 = {{}} -- 定義個空的二維 tab tab2 = {{"北京", "上海"}, {"java", "go"}} print(tab2[1][1], tab2[2][2]) -- 輸出 北京 go
元表( metatable
)是一個表,它是使用鍵集和相關元方法來修改附加到的表的行爲。這些元方法是強大的Lua功能。
__index
這是 metatable 最經常使用的鍵。當你經過鍵來訪問 table 的時候,若是這個鍵沒有值,那麼Lua就會尋找該table的metatable,是否存在__index 方法。若是存在就會調用它,不存在就會返回 nil
__newindex
若是對 table 裏面的數據進行新增,例如 table["xxx"] = 300
那麼就會調用 metatable __newindex
方法__call
若是對 table 直接調用,那麼會調用 metatable 中的 __call
這個方法, 例如調用tab()
。__tostring
若是堆 table 直接輸出,那麼會調用 metatable 中的__tostring
這個方法,例如print(tab)
-- __index 索引 tab = {10, 20, 30} tab2 = {} -- __newindex 賦值方式1 metatab = { -- __index 若是tab中不存在key,則調用__index。 __index = function(t, k) print(string.format( "調用了 tab 不存在的索引,key=%s", k)) end, -- __newindex = tab2, -- 賦值方式1,直接賦值給 tab2 例如調用 xxx["user"]=1 ,那麼tab2["user"]的值爲1 __newindex = function(t, k, v) -- 賦值方式2方法自定義調用 rawset(t, k, v) -- 賦值給 t print(string.format( "新增了tab不存在的索引,k=%s, v=%s", k, v)) end, __call = function(t, arg1, arg2) print(string.format("調用了__call, arg1=%s, arg2=%s,返回了true", arg1, arg2)) end, __tostring = function(t) print("調用了直接輸出 __tostring") -- 直接輸出 tab 會調用這個方法 return "call __tostring" end, __le = function(t, newtab) --兩個表調用了 <= 比較運算符 print("調用了兩個表==運算符") return true end, __add = function(t, newtab) -- 根據本身的場景來處理,這裏處理的是兩個表合併 print("調用了__add") for k, v in pairs(newtab) do -- 尚未學習 for,先不要關心 table.insert(t, v) end return t end, } -- 對指定 table 設置元表(metatable) restab = setmetatable(tab, metatab) restab[20] = 2000 -- 調用 __index 方法 restab(32, 45) -- 調用 __call 方法 print(restab) -- 調用 __tostring newtab = {80, 90} res = restab + newtab -- 調用兩個表想加 if restab <= n then -- 調用了 __le -- do something end
操做運算符
模式 | 描述 |
---|---|
__add | 對應的運算符 '+'. |
__sub | 對應的運算符 '-'. |
__mul | 對應的運算符 '*'. |
__div | 對應的運算符 '/'. |
__mod | 對應的運算符 '%'. |
__unm | 對應的運算符 '-'. |
__concat | 對應的運算符 '..'. |
__eq | 對應的運算符 '=='. |
__lt | 對應的運算符 '<'. |
__le | 對應的運算符 '<='. |
好吧,原表終於學完了,若是你在學習中也進行實際操做,那麼必定超過30分鐘了。不過堅持一下,剩下的部分很快了。有了元表的基礎就能夠學習面向對象了
Lua 的循環有3中分別是 for, while, repeat unitl。其中 for 包括基本 for 與泛型 for,至關於其餘語言的 for in 或者 foreach。
-- 最簡單的循環 a = 5 while a >= 0 do print(a) -- 輸出 5 4 3 2 1 0 a = a -1 end
for 循環分爲兩種,數值型和泛型,數值型比其餘語言更簡介,也有點怪怪的,熟悉就好。
數值型的 for 循環長這樣
for var=exp1,exp2,exp3 do <執行體> end
exp1表明初始化的數值,exp2表明結束條件,注意這裏沒有比較運算符,exp3表明步長,也能夠省略。
for i = 1, 5, 1 do print(i) -- 輸出 1 2 3 4 5 end -- 步長是2的狀況 for i = 1, 5, 2 do print(i) -- 輸出 1 3 5 end
步長也就是 exp3能夠省略,默認步長是1
for i = 1, 5 do print(i) -- 輸出 1 2 3 4 5 end
這裏須要注意一點,與其餘語言不同 for的三個表達式在循環開始前一次性求值,之後再也不進行求值。好比上面的f(x)只會在循環開始前執行一次,其結果用在後面的循環中。
泛型循環有兩個迭代函數 pairs 與 ipairs,他們的區別是 ipairs遇到nil會中止,pairs會輸出nil值而後繼續下去,文字看不動不要緊,看代碼。
table = {"1", "2", "3", nil, "5"} for k, v in pairs(table) do print("k="..k..",val="..v) end
這個栗子會輸出
k=1,val=1 k=2,val=2 k=3,val=3 k=5,val=5
-- ipairs 若是文字沒有理解啥意思,能夠對比一下上一個栗子與這個栗子的結果 table = {"1", "2", "3", nil, "5"} for k, v in ipairs(table) do print("k="..k..",val="..v) end
這個栗子會輸出
k=1,val=1 k=2,val=2 k=3,val=3
-- repeat a = 1 repeat -- 開始重複 print(a) a = a +1 until(a>3) -- 條件 直到條件判斷語句(condition)爲 true 纔會中止執行。
這個栗子會輸出
1 2 3
趁熱打鐵,迭代器與 for 循環一塊兒結合着理解會很好理解。其實上個例子中 pairs 與 ipairs 就是兩個泛型迭代器,lua 內部都給咱們實現好了。
迭代器(iterator)是一種對象,它可以用來遍歷標準模板庫容器中的部分或所有元素,每一個迭代器對象表明容器中的肯定的地址。
在 Lua 中迭代器是一種支持指針類型的結構,它能夠遍歷集合的每個元素。
泛型 for 在本身內部保存迭代函數,實際上它保存三個值:迭代函數、狀態常量、控制變量。
無狀態的迭代器是指不保留任何狀態的迭代器,所以在循環中咱們能夠利用無狀態迭代器避免建立閉包花費額外的代價。
-- 迭代函數 state 只有在初始化的時候賦值 function square(max, add) if add >= max then -- 當累加器到達最大返回 nil 外部會跳出循環 return nil else add = add+1 -- lua 沒有 ++ return add, add*add -- 返回兩個參數與 go 類似,返回當前的值和乘 end end for i, j in square , 5, 0 do -- 調用自定義迭代器 print(i, j) end
這個栗子輸出
1 1 2 4 3 9 4 16 5 25 6 36 7 49 8 64 9 81
不少狀況下,迭代器須要保存多個狀態信息而不是簡單的狀態常量和控制變量,最簡單的方法是使用閉包,還有一種方法就是將全部的狀態信息封裝到 table 內,將 table 做爲迭代器的狀態常量,由於這種狀況下能夠將全部的信息存放在 table 內,因此迭代函數一般不須要第二個參數
-- 多狀態迭代器 tab = {"google", "baidu"} function square (con) local index = 0 local count = #con -- #表明計算長度 return function() -- 這是一個匿名函數會直接調用 index = index + 1 if index <= count then -- index 小於總長度 return con[index] -- 返回這個 val end end end for val in square(tab) do print(val) -- 輸出 google baidu end
支持返回多個返回值
function op(key, val) return key, val -- 支持兩個參數返回 end arg1, arg2 = op("user", 100)
參數支持傳遞函數
-- 參數函數傳遞的方式 function sum(a, b, func) return func(a, b) end function func(a, b) return a + b end val = sum(10, 20, func) print(val) -- 輸出30
匿名函數的方式傳遞
function max(a, b, func) max = func(a, b) print(max) -- 輸出 39 end getmax = function(a, b) if a > b then return a else return b end end max(10, 39, getmax)
可變參數
function args(...) local arg = {...} -- 局部定義不然會有重複賦值 print("總共傳入 " .. select("#",...) .. " 個數") print(arg[1], arg[2], arg[3]) end args(10, 20, 30)
這個栗子會輸出
總共傳入 3 個數 10 20 30
.
的定義定義方式,不能直接使用 self
-- table 引用類型 phone = { name = "馬化騰", memory = "128GB" } phone.call = function(self) str = string.format( "%s 正在打電話", self.name) print(str) end phone.call(phone) -- 輸出 馬化騰 正在打電話
:
的定義方式
phone = { name = "馬化騰", pname = "李彥宏" } function phone:call() str = string.format( "%s 正在打電話", self.name) print(str) end function phone:say(name) self.pname = name str = string.format( "%s 說%s你趕忙充錢", self.name, self.pname) print(str) end phone:call() phone:say("馬雲")
輸出
馬化騰 正在打電話 馬化騰 說馬雲你趕忙充錢
完整的面向對象實例
-- table 引用類型 phone = { name = "馬化騰", } function phone:call() str = string.format("%s 正在打電話", self.name) print(str) end function phone:new(sel) sel = sel or {} -- 因爲 table 是引用類型,這裏須要從新賦值 setmetatable(sel, {__index = self}) -- 將 self 賦給 sel return sel end mahuat = phone:new() mayun = phone:new() mahuat.name = "馬化騰" mayun.name = "馬雲" mahuat:call() -- 注意 :call()的形式,不然沒法得到到 self mayun:call()
輸出
馬化騰 正在打電話 馬雲 正在打電話
看代碼,認真看,沒那麼繁瑣
Phone = { name = "", } function Phone:call() -- 定義基本方法 str = string.format("%s 正在打電話", self.name) print(str) end function Phone:new(sel) sel = sel or {} setmetatable(sel, {__index = self}) -- 將 self 賦給 sel return sel end -- Mahuat繼承了 Phone 方法 Mahuat = Phone:new() function Mahuat:say() self.name = "馬化騰" self:call() -- 調用父類的方法 str = string.format("%s 說趕忙充錢!", self.name) print(str) end function Mahuat:new(sel) sel = sel or Phone:new() setmetatable(sel, self) self.__index = self return sel end Mahuat:say() -- 調用本身的方法 -- Mayun繼承了 Phone 方法 Mayun = Phone:new() function Mayun:say() self.name = "馬雲" self:call() -- 調用父類的方法 str = string.format("%s 說快還花唄!", self.name) print(str) end function Mayun:new(sel) sel = sel or Phone:new() setmetatable(sel, self) self.__index = self return sel end Mayun:say() -- 調用本身的方法 -- 派生重寫 liyanhong = Phone:new() function liyanhong:call() print("咱們不生產假藥!咱們只是假藥的搬運工~ ") end -- 重寫了 call 方法 function liyanhong:say() self.name = "李彥宏" self:call() -- 調用父類的方法 str = string.format("%說歐耶!", self.name) print(str) end function liyanhong:new(sel) sel = sel or Phone:new() setmetatable(sel, self) self.__index = self return sel end liyanhong:say() -- 調用本身的方法
這個例子輸出
馬化騰 正在打電話 馬化騰 說趕忙充錢! 馬雲 正在打電話 馬雲 說快還花唄! 咱們不生產假藥!咱們只是假藥的搬運工~ 李彥宏 說歐耶!
恭喜您讀到這裏,實際上尚未完!抱歉了,😄
Lua 的最基礎部分就學完了,還須要一個篇寫其餘的操做,例如文本操做,函數庫調用。