柚子快報(bào)激活碼778899分享:Lua腳本語言(持續(xù)更新)
柚子快報(bào)激活碼778899分享:Lua腳本語言(持續(xù)更新)
初識(shí)Lua
概念:Lua是一種輕量小巧的腳本語言,用標(biāo)準(zhǔn)C語言編寫并以源代碼形式開放,其設(shè)計(jì)目的是為了嵌入應(yīng)用程序中,從而為應(yīng)用程序提供靈活的擴(kuò)展和定制功能。
特性:
輕量級(jí): 它用標(biāo)準(zhǔn)C語言編寫并以源代碼形式開放,編譯后僅僅一百余K,可以很方便的嵌入別的程序里。
可擴(kuò)展: Lua提供了非常易于使用的擴(kuò)展接口和機(jī)制:由宿主語言(通常是C或C++)提供這些功能,Lua可以使用它們,就像是內(nèi)置的功能一樣。
支持面向過程(procedure-oriented)編程和函數(shù)式編程(functional programming);
自動(dòng)內(nèi)存管理;
只提供了一種通用類型的表(table),用它可以實(shí)現(xiàn)數(shù)組,哈希表,集合,對(duì)象;
語言內(nèi)置模式匹配;
閉包(closure);
函數(shù)也可以看做一個(gè)值;
提供多線程(協(xié)同程序,并非操作系統(tǒng)所支持的線程)支持;
通過閉包和table可以很方便地支持面向?qū)ο缶幊趟枰囊恍╆P(guān)鍵機(jī)制,比如數(shù)據(jù)抽象,虛函數(shù),繼承和重載等。
應(yīng)用場景:
游戲開發(fā)獨(dú)立應(yīng)用腳本W(wǎng)eb應(yīng)用腳本擴(kuò)展和數(shù)據(jù)庫插件如:MySQL Proxy 和 MySQL WorkBench安全系統(tǒng)(如入侵檢測系統(tǒng))
安裝Lua(Window系統(tǒng))
Window下可以使用一個(gè)叫"SciTE"的IDE環(huán)境來執(zhí)行l(wèi)ua程序,下載地址為:
Github下載地址:https://github.com/rjpcomputing/luaforwindows/releases
選第一個(gè)(LuaForWindow_v5.1.5-52.exe)
Google Code下載地址 : https://code.google.com/p/luaforwindows/downloads/list
雙擊安裝后即可在該環(huán)境下編寫Lua程序并運(yùn)行。
Lua基礎(chǔ)語法
?注意:可以在命令行窗口中使用lua -i來啟用交互式編程(這個(gè)命令其實(shí)就是啟動(dòng)了上圖中藍(lán)色圖標(biāo)的lua應(yīng)用程序)。
交互式編程
腳本式編程
先創(chuàng)建一個(gè)以lua結(jié)尾的文件(如LuaStudy.lua文件),用SciTE打開該文件,再點(diǎn)擊"RunPrograme"即可運(yùn)行。
我們還可以cmd到該lua文件的位置,然后執(zhí)行如下命令:
單行注釋:--
多行注釋:--[[注釋內(nèi)容--]]
標(biāo)識(shí)符:標(biāo)示符以一個(gè)字母A到Z或a到z或下劃線 _ 開頭后加上0個(gè)或多個(gè)字母,下劃線,數(shù)字(0到9)。
注意:
最好不要使用下劃線加大寫字母的標(biāo)示符,因?yàn)長ua的保留字也是這樣的。
Lua不允許使用特殊字符如 @, $, 和 % 來定義標(biāo)示符。Lua是一個(gè)區(qū)分大小寫的編程語言。
關(guān)鍵字:以下列出了Lua的保留關(guān)鍵詞。保留關(guān)鍵字不能作為常量或變量或其他用戶自定義標(biāo)示符。
andbreakdoelseelseifendfalseforfunctionifinlocalnilnotorrepeatreturnthentrueuntilwhilegoto
注意:一般約定,以下劃線開頭連接一串大寫字母的名字(比如 _VERSION)被保留用于Lua內(nèi)部全局變量。
全局變量:
在默認(rèn)情況下,變量總是認(rèn)為是全局的。
全局變量不需要聲明,給一個(gè)變量賦值后即創(chuàng)建了這個(gè)全局變量,訪問一個(gè)沒有初始化的全局變量也不會(huì)出錯(cuò),只不過得到的結(jié)果是:nil
print(value)
value = "賦值"
print(value)
value = nil --刪除value
print(value)
--[[
輸出結(jié)果:
nil
賦值
nil
--]]
?
如果你想刪除一個(gè)全局變量,只需要將變量賦值為nil。
這樣變量value就好像從沒被使用過一樣。換句話說, 當(dāng)且僅當(dāng)一個(gè)變量不等于nil時(shí),這個(gè)變量即存在。
Lua數(shù)據(jù)類型
Lua是動(dòng)態(tài)類型語言,變量不要類型定義,只需要為變量賦值。 值可以存儲(chǔ)在變量中,作為參數(shù)傳遞或結(jié)果返回。
Lua中有8個(gè)基本類型分別為:nil、boolean、number、string、userdata、function、thread和table。
數(shù)據(jù)類型描述nil這個(gè)最簡單,只有值nil屬于該類,表示一個(gè)無效值(在條件表達(dá)式中相當(dāng)于false)。boolean包含兩個(gè)值:false和true。number表示雙精度類型的實(shí)浮點(diǎn)數(shù)string字符串由一對(duì)雙引號(hào)或單引號(hào)來表示function由C或Lua編寫的函數(shù)userdata表示任意存儲(chǔ)在變量中的C數(shù)據(jù)結(jié)構(gòu)thread表示執(zhí)行的獨(dú)立線路,用于執(zhí)行協(xié)同程序tableLua中的表(table)其實(shí)是一個(gè)"關(guān)聯(lián)數(shù)組"(associative arrays),數(shù)組的索引可以是數(shù)字、字符串或表類型。在Lua里,table的創(chuàng)建是通過"構(gòu)造表達(dá)式"來完成,最簡單構(gòu)造表達(dá)式是{},用來創(chuàng)建一個(gè)空表。
可以使用type函數(shù)測試給定變量或者值的類型:
print(type(nil))
print(type(true))
print(type(1000))
print(type("中國"))
print(type(type))
print(type({"A","B"}))
--[[
輸出結(jié)果:
nil
boolean
number
string
function
table
--]]
nil(空)
nil類型表示一種沒有任何有效值,它只有一個(gè)值nil,例如打印一個(gè)沒有賦值的變量,便會(huì)輸出一個(gè) nil 值。
對(duì)于全局變量和table,nil還有一個(gè)"刪除"作用,給全局變量或者table表里的變量賦一個(gè)nil值,等同于把它們刪掉。
boolean(布爾值)
boolean類型只有兩個(gè)可選值:true(真)和false(假),Lua把false和nil看作是false,其他的都為 true,數(shù)字0也是true。
number(數(shù)字)
Lua默認(rèn)只有一種number類型 -- double(雙精度)類型(默認(rèn)類型可以修改luaconf.h里的定義)。
string(字符串)
字符串由一對(duì)雙引號(hào)或單引號(hào)來表示。也可以用2個(gè)方括號(hào)"[[內(nèi)容]]"來表示"一塊"字符串。
在對(duì)一個(gè)數(shù)字字符串上進(jìn)行算術(shù)操作時(shí),Lua 會(huì)嘗試將這個(gè)數(shù)字字符串轉(zhuǎn)成一個(gè)數(shù)字:
print("3" + 7)
print("3" + "7")
print("3 + 7")
print("A" + 10)
--[[
輸出結(jié)果:
10
10
3 + 7
lua: LuaStudy.lua:30: attempt to perform arithmetic on a string value
stack traceback:
LuaStudy.lua:30: in main chunk
[C]: ?
--]]
字符串連接使用:..
獲取字符串長度:#、string.len、utf8.len
str01 = "AB"
str02 = "我是中國人"
print(str01..str02)
print(#str01) --輸出:2
print(#str02) --輸出:10
print(string.len(str01)) --輸出:2
print(string.len(str02)) --輸出:10
print(utf8.len(str01)) --輸出:2
print(utf8.len(str02)) --輸出:5
function(函數(shù))
在Lua中,函數(shù)是被看作是"第一類值(First-Class Value)",函數(shù)可以存在變量里:
function myFun(value)
if value == 0 then
return 1
else
return value*myFun(value-1)
end
end
print(myFun(3)) --輸出:6
local tempFun = myFun
print(tempFun(5)) --輸出:120
function可以以匿名函數(shù)的方式通過參數(shù)傳遞:
function myFun(tab,fun)
for k,v in pairs(tab) do
print(fun(k,v))
end
end
tab = {"A","B","C","D"}
myFun(tab,function(k,v)
return k.." = "..v
end
)
--[[
輸出結(jié)果:
1 = A
2 = B
3 = C
4 = D
--]]
table(表)
在Lua里,table的創(chuàng)建是通過"構(gòu)造表達(dá)式"來完成,最簡單構(gòu)造表達(dá)式是{},用來創(chuàng)建一個(gè)空表。也可以在表里添加一些數(shù)據(jù),直接初始化表:
--創(chuàng)建一個(gè)空的table
local tab1 = {}
?
--直接初始表
local tab2 = {"apple", "pear", "orange", "grape"}
Lua中的表(table)其實(shí)是一個(gè)"關(guān)聯(lián)數(shù)組"(associative arrays),數(shù)組的索引可以是數(shù)字或者是字符串。
tab = {}
key = 10
tab["key"] = "value"
tab[key] = 50
tab[key] = tab[key]*2
for k,v in pairs(tab) do
print(k.." = "..v)
end
print(tab.key) --鍵為字符串的話可以這樣訪問
--[[
輸出結(jié)果:
key = value
10 = 100
value
--]]
注意:
不同于其他語言的數(shù)組把0作為數(shù)組的初始索引,在Lua里表的默認(rèn)初始索引一般以1開始。
table長度大小是不固定的,有新數(shù)據(jù)添加時(shí)table長度會(huì)自動(dòng)增長,沒初始的table都是nil。
thread(線程)
在Lua里,最主要的線程是協(xié)同程序(coroutine)。它跟線程(thread)差不多,擁有自己獨(dú)立的棧、局部變量和指令指針,可以跟其他協(xié)同程序共享全局變量和其他大部分東西。
線程跟協(xié)程的區(qū)別:線程可以同時(shí)多個(gè)運(yùn)行,而協(xié)程任意時(shí)刻只能運(yùn)行一個(gè),并且處于運(yùn)行狀態(tài)的協(xié)程只有被掛起(suspend)時(shí)才會(huì)暫停。
userdata(自定義類型)
userdata是一種用戶自定義數(shù)據(jù),用于表示一種由應(yīng)用程序或C/C++語言庫所創(chuàng)建的類型,可以將任意C/C++的任意數(shù)據(jù)類型的數(shù)據(jù)(通常是struct和指針)存儲(chǔ)到Lua變量中調(diào)用。
Lua變量
變量在使用前,需要在代碼中進(jìn)行聲明,即創(chuàng)建該變量。
編譯程序執(zhí)行代碼之前編譯器需要知道如何給語句變量開辟存儲(chǔ)區(qū),用于存儲(chǔ)變量的值。
Lua變量有三種類型:全局變量、局部變量、表中的域。
Lua中的變量全是全局變量,哪怕是語句塊或是函數(shù)里,除非用local顯式聲明為局部變量。
局部變量的作用域?yàn)閺穆暶魑恢瞄_始到所在語句塊結(jié)束。
變量的默認(rèn)值均為nil。
--StudyLua.lua腳本
a = 5 --全局變量
local b = 5 --局部變量
function MyFun()
c = 5 --全局變量
local d = 6 --局部變量
end
MyFun()
print(c,d) --輸出:5和nil
do
local a = 6 --局部變量
b = 6 --重新賦值局部變量
print(a,b) --輸出:6和6
end
print(a,b) --輸出:5和6
賦值語句
賦值是改變一個(gè)變量的值和改變表域的最基本的方法。
s = "hello" .. "world"
tab.n = tab.n + 1
Lua可以對(duì)多個(gè)變量同時(shí)賦值,變量列表和值列表的各個(gè)元素用逗號(hào)分開,賦值語句右邊的值會(huì)依次賦給左邊的變量。
a, b = 10, 2*x <--> a=10; b=2*x
遇到賦值語句Lua會(huì)先計(jì)算右邊所有的值然后再執(zhí)行賦值操作,所以我們可以這樣進(jìn)行交換變量的值:
x, y = y, x -- swap 'x' for 'y'
a[i], a[j] = a[j], a[i] -- swap 'a[i]' for 'a[j]'
當(dāng)變量個(gè)數(shù)和值的個(gè)數(shù)不一致時(shí),Lua會(huì)一直以變量個(gè)數(shù)為基礎(chǔ)采取以下策略:
a. 變量個(gè)數(shù) > 值的個(gè)數(shù) 按變量個(gè)數(shù)補(bǔ)足nil
b. 變量個(gè)數(shù) < 值的個(gè)數(shù) 多余的值會(huì)被忽略
a, b, c = 0, 1
print(a,b,c) ? ? ? ? ? ? --> 0 ? 1 ? nil
?
a, b = a+1, b+1, b+2 ? ? -- b+2會(huì)被忽略掉
print(a,b) ? ? ? ? ? ? ? --> 1 ? 2
?
a, b, c = 0
print(a,b,c) ? ? ? ? ? ? --> 0 ? nil ? nil
上面最后一個(gè)例子是一個(gè)常見的錯(cuò)誤情況,注意:如果要對(duì)多個(gè)變量賦值必須依次對(duì)每個(gè)變量賦值。
a, b, c = 0, 0, 0
print(a,b,c) --> 0 0 0
多值賦值經(jīng)常用來交換變量,或?qū)⒑瘮?shù)調(diào)用返回給變量:
a,b = fun() --fun()返回兩個(gè)值,第一個(gè)賦給a,第二個(gè)賦給b。
應(yīng)該盡可能的使用局部變量,有兩個(gè)好處:
1. 避免命名沖突。2. 訪問局部變量的速度比全局變量更快。
索引
對(duì)table的索引使用方括號(hào) []。Lua也提供了.操作。
t[i]
t.i -- 當(dāng)索引為字符串類型時(shí)的一種簡化寫法
gettable_event(t,i) -- 采用索引訪問本質(zhì)上是一個(gè)類似這樣的函數(shù)調(diào)用
site = {}
site["key"] = "www.runoob.com"
print(site["key"]) --輸出:www.runoob.com
print(site.key) --輸出:www.runoob.com
Lua循環(huán)
一組被重復(fù)執(zhí)行的語句稱之為循環(huán)體,能否繼續(xù)重復(fù),決定循環(huán)的終止條件。
循環(huán)結(jié)構(gòu)是在一定條件下反復(fù)執(zhí)行某段程序的流程結(jié)構(gòu),被反復(fù)執(zhí)行的程序被稱為循環(huán)體。
循環(huán)語句是由循環(huán)體及循環(huán)的終止條件兩部分組成的。
Lua語言提供了以下幾種循環(huán)處理方式:
循環(huán)類型描述while 循環(huán)在條件為true時(shí),讓程序重復(fù)地執(zhí)行某些語句。執(zhí)行語句前會(huì)先檢查條件是否為true。for 循環(huán)重復(fù)執(zhí)行指定語句,重復(fù)次數(shù)可在for語句中控制。repeat...until?重復(fù)執(zhí)行循環(huán),直到 指定的條件為真時(shí)為止循環(huán)嵌套可以在循環(huán)內(nèi)嵌套一個(gè)或多個(gè)循環(huán)語句(while do ... end;for ... do ... end;repeat ... until;)
?Lua while循環(huán)
Lua編程語言中while循環(huán)語句在判斷條件為true時(shí)會(huì)重復(fù)執(zhí)行循環(huán)體語句。
語法
Lua編程語言中while循環(huán)語法:
while(condition) do
statements
end
statements(循環(huán)體語句)可以是一條或多條語句,condition(條件)可以是任意表達(dá)式,在 condition(條件)為true時(shí)執(zhí)行循環(huán)體語句。?
流程圖如下:
在以上流程圖中我們可以看出在condition(條件)為 false 時(shí)會(huì)跳過當(dāng)前循環(huán)并開始腳本執(zhí)行緊接著的語句。?
實(shí)例
value = 0
while(value < 5) do
print(value)
value = value + 1
end
--[[
輸出結(jié)果:
0
1
2
3
4
--]]
Lua for循環(huán)
Lua編程語言中for循環(huán)語句可以重復(fù)執(zhí)行指定語句,重復(fù)次數(shù)可在for語句中控制。
Lua編程語言中for語句有兩大類:
數(shù)值for循環(huán)泛型for循環(huán)
數(shù)值for循環(huán)
Lua編程語言中數(shù)值for循環(huán)語法格式:
for i = exp1,exp2,exp3 do
--執(zhí)行體
end
i從exp1變化到exp2,每次變化以exp3為步長遞增i,并執(zhí)行一次 "執(zhí)行體"。exp3是可選的,如果不指定,默認(rèn)為1。
for i=1,f(x) do
? ? print(i)
end
?
for i=10,1,-1 do
? ? print(i)
end
for的三個(gè)表達(dá)式在循環(huán)開始前一次性求值,以后不再進(jìn)行求值。比如上面的f(x)只會(huì)在循環(huán)開始前執(zhí)行一次,其結(jié)果用在后面的循環(huán)中。
驗(yàn)證如下:
function fun(x)
print("fun")
return x*2
end
for i=1,fun(2) do
print(i)
end
?
--[[
輸出結(jié)果:
fun
1
2
3
4
--]]
可以看到函數(shù)f(x)只在循環(huán)開始前執(zhí)行一次。?
泛型for循環(huán)
泛型for循環(huán)通過一個(gè)迭代器函數(shù)來遍歷所有值,類似java中的foreach語句。
Lua編程語言中泛型for循環(huán)語法格式:
tab = {"A","B","C"}
for k,v in ipairs(tab) do
print(k.." = "..v)
end
--[[
輸出結(jié)果:
1 = A
2 = B
3 = C
--]]
拓展:pairs和ipairs的異同。
相同點(diǎn):
兩者都能遍歷集合。兩者都會(huì)優(yōu)先輸出沒有鍵(鍵為整數(shù))的值。
不同點(diǎn):
1.pairs
集合中整數(shù)和非整數(shù)的鍵都會(huì)遍歷。如果遇到nil,它會(huì)忽略該鍵值對(duì)(就像不存在一樣),然后繼續(xù)遍歷集合。
2.ipairs
從集合的第1項(xiàng)開始,逐個(gè)輸出其鍵值對(duì)。如果遇到非整數(shù)的鍵,它會(huì)忽略該鍵值對(duì)(就像不存在一樣),然后繼續(xù)遍歷集合。如果遇到nil,它將終止遍歷。
Lua repeat...until循環(huán)
Lua編程語言中repeat...until循環(huán)語句不同于for和while循環(huán),for和while循環(huán)的條件語句在當(dāng)前循環(huán)執(zhí)行開始時(shí)判斷,而repeat...until循環(huán)的條件語句在當(dāng)前循環(huán)結(jié)束后判斷。
語法
Lua編程語言中repeat...until循環(huán)語法格式:
repeat
statements
until(condition)
我們注意到循環(huán)條件判斷語句(condition)在循環(huán)體末尾部分,所以在條件進(jìn)行判斷前循環(huán)體都會(huì)執(zhí)行一次。
如果條件判斷語句(condition)為 false,循環(huán)會(huì)重新開始執(zhí)行,直到條件判斷語句(condition)為true才會(huì)停止執(zhí)行。
Lua repeat...until循環(huán)流程圖如下:
實(shí)例
value = 0
repeat
print("value = "..value)
value = value + 1
until(value > 5)
--[[
輸出結(jié)果:
value = 0
value = 1
value = 2
value = 3
value = 4
value = 5
--]]
Lua循環(huán)嵌套
Lua編程語言中允許循環(huán)中嵌入循環(huán)。以下實(shí)例演示了Lua循環(huán)嵌套的應(yīng)用。
語法
Lua編程語言中for循環(huán)嵌套語法格式:
for init,max/min value, increment do
for init,max/min value, increment do
statements
end
statements
end
Lua編程語言中while循環(huán)嵌套語法格式:
while(condition) do
while(condition) do
statements
end
statements
end
Lua編程語言中repeat...until循環(huán)嵌套語法格式:
repeat
statements
repeat
statements
until(condition)
until(condition)
除了以上同類型循環(huán)嵌套外,我們還可以使用不同的循環(huán)類型來嵌套,如for循環(huán)體中嵌套while循環(huán)。?
實(shí)例
以下實(shí)例使用了for循環(huán)嵌套:
j =2
for i=2,10 do
for j=2,(i/j) , 2 do
if(not(i%j))
then
break
end
if(j > (i/j))then
print("i 的值為:",i)
end
end
end
--[[
輸出結(jié)果:
i 的值為:8
i 的值為:9
i 的值為:10
--]]
循環(huán)控制語句
循環(huán)控制語句用于控制程序的流程, 以實(shí)現(xiàn)程序的各種結(jié)構(gòu)方式。
Lua支持以下循環(huán)控制語句:
控制語句描述break 語句退出當(dāng)前循環(huán)或語句,并開始腳本執(zhí)行緊接著的語句。goto 語句將程序的控制點(diǎn)轉(zhuǎn)移到一個(gè)標(biāo)簽處。
?Lua break語句
Lua編程語言break語句插入在循環(huán)體中,用于退出當(dāng)前循環(huán)或語句,并開始腳本執(zhí)行緊接著的語句。
如果你使用循環(huán)嵌套,break語句將停止最內(nèi)層循環(huán)的執(zhí)行,并開始執(zhí)行的外層的循環(huán)語句。
語法
Lua編程語言中break語句語法格式:break
流程圖:
實(shí)例
以下實(shí)例執(zhí)行while循環(huán),在變量a小于20時(shí)輸出a的值,并在a大于15時(shí)終止執(zhí)行循環(huán):
a = 10
while( a < 20 ) do
print("a 的值為: "..a)
a = a + 1
if(a > 15) then
break --使用 break 語句終止循環(huán)
end
end
--[[
輸出結(jié)果:
a 的值為: 10
a 的值為: 11
a 的值為: 12
a 的值為: 13
a 的值為: 14
a 的值為: 15
--]]
Lua goto語句
Lua語言中的goto語句允許將控制流程無條件地轉(zhuǎn)到被標(biāo)記的語句處。
語法
語法格式如下所示:goto Label
Label的格式為::: Label ::
實(shí)例
以下實(shí)例在判斷語句中使用 goto:
local a = 1
::label:: print("--- goto label ---")
a = a+1
if a < 3 then
? ? goto label ? -- a 小于 3 的時(shí)候跳轉(zhuǎn)到標(biāo)簽 label
end
--[[
輸出結(jié)果:
--- goto label ---
--- goto label ---
--]]
從輸出結(jié)果可以看出,多輸出了一次 --- goto label ---?
以下實(shí)例演示了可以在lable中設(shè)置多個(gè)語句:
i = 0
::s1:: do
print(i)
i = i + 1
end
if i > 3 then
os.exit() -- i 大于 3 時(shí)退出
end
goto s1
--[[
輸出結(jié)果:
0
1
2
3
--]]
使用goto語句,我們可以實(shí)現(xiàn)continue的功能:
for i=1, 3 do
if i <= 2 then
print(i, "yes continue")
goto continue
end
print(i, " no continue")
::continue::
print([[i'm end]])
end
--[[
輸出結(jié)果:
1 yes continue
i'm end
2 yes continue
i'm end
3 no continue
i'm end
--]]
無限循環(huán)
在循環(huán)體中如果條件永遠(yuǎn)為true循環(huán)語句就會(huì)永遠(yuǎn)執(zhí)行下去,以下以while循環(huán)為例:
while(true) do
print("循環(huán)將永遠(yuǎn)執(zhí)行下去")
end
Lua流程控制
Lua編程語言流程控制語句通過程序設(shè)定一個(gè)或多個(gè)條件語句來設(shè)定。在條件為true時(shí)執(zhí)行指定程序代碼,在條件為false時(shí)執(zhí)行其他指定代碼。
以下是典型的流程控制流程圖:
控制結(jié)構(gòu)的條件表達(dá)式結(jié)果可以是任何值,Lua認(rèn)為false和nil為假,true和非nil為真。
注意:在Lua中0為true;
Lua提供了以下控制結(jié)構(gòu)語句:
語句描述if 語句if語句由一個(gè)布爾表達(dá)式作為條件判斷,其后緊跟其他語句組成。if...else 語句if語句可以與else語句搭配使用, 在if條件表達(dá)式為false時(shí)執(zhí)行else語句代碼。if 嵌套語句你可以在if或else if中使用一個(gè)或多個(gè)if或else if語句。
?Lua if語句
Lua if語句由一個(gè)布爾表達(dá)式作為條件判斷,其后緊跟其他語句組成。
Lua if語句語法格式如下:
if(布爾表達(dá)式) then
--[ 在布爾表達(dá)式為 true 時(shí)執(zhí)行的語句 --]
end
在布爾表達(dá)式為true時(shí),if中的代碼塊會(huì)被執(zhí)行,在布爾表達(dá)式為false時(shí),緊跟在if語句end之后的代碼會(huì)被執(zhí)行。
Lua認(rèn)為false和nil為假,true和非nil為真。要注意的是Lua中0為true。
if 語句流程圖如下:
?實(shí)例:以下實(shí)例用于判斷變量a的值是否小于20。
value = 10
if(value < 20) then
print("value 小于 20")
end
print("value = "..value)
--[[
輸出結(jié)果:
value 小于 20
value = 10
--]]
Lua if...else語句
if...else語句
Lua if語句可以與else語句搭配使用, 在if條件表達(dá)式為false時(shí)執(zhí)行else語句代碼塊。
Lua if...else語句語法格式如下:
if(布爾表達(dá)式) then
--[ 布爾表達(dá)式為 true 時(shí)執(zhí)行該語句塊 --]
else
--[ 布爾表達(dá)式為 false 時(shí)執(zhí)行該語句塊 --]
end
在布爾表達(dá)式為true時(shí),if中的代碼塊會(huì)被執(zhí)行,在布爾表達(dá)式為false時(shí),else的代碼塊會(huì)被執(zhí)行。
Lua認(rèn)為false和nil為假,true和非nil為真。要注意的是Lua中0為true。
if語句流程圖如下:
實(shí)例:以下實(shí)例用于判斷變量a的值。
--[ 定義變量 --]
a = 100;
--[ 檢查條件 --]
if( a < 20 ) then
--[ if 條件為 true 時(shí)執(zhí)行該語句塊 --]
print("a 小于 20" )
else
--[ if 條件為 false 時(shí)執(zhí)行該語句塊 --]
print("a 大于 20" )
end
print("a 的值為 :"..a)
--[[
輸出結(jié)果:
a 大于 20
a 的值為 :100
--]]
if...elseif...else語句
Lua if語句可以與elseif...else語句搭配使用, 在if條件表達(dá)式為false時(shí)執(zhí)行elseif...else語句代碼塊,用于檢測多個(gè)條件語句。
Lua if...elseif...else語句語法格式如下:
if( 布爾表達(dá)式 1) then
--[ 在布爾表達(dá)式 1 為 true 時(shí)執(zhí)行該語句塊 --]
elseif( 布爾表達(dá)式 2) then
--[ 在布爾表達(dá)式 2 為 true 時(shí)執(zhí)行該語句塊 --]
elseif( 布爾表達(dá)式 3) then
--[ 在布爾表達(dá)式 3 為 true 時(shí)執(zhí)行該語句塊 --]
else
--[ 如果以上布爾表達(dá)式都不為 true 則執(zhí)行該語句塊 --]
end
實(shí)例:以下實(shí)例對(duì)變量a的值進(jìn)行判斷。
a = 100
if( a == 10 ) then
print("a 的值為 10")
elseif( a == 20 ) then
print("a 的值為 20")
elseif( a == 30 ) then
print("a 的值為 30")
else
print("沒有匹配 a 的值")
end
print("a 的真實(shí)值為: "..a)
--[[
輸出結(jié)果:
沒有匹配 a 的值
a 的真實(shí)值為: 100
--]]
Lua if嵌套語句
Lua if語句允許嵌套, 這就意味著你可以在一個(gè)if或else if語句中插入其他的if或else if語句。
Lua if嵌套語句語法格式如下:
if( 布爾表達(dá)式 1) then
--布爾表達(dá)式 1 為 true 時(shí)執(zhí)行該語句塊
if(布爾表達(dá)式 2) then
--布爾表達(dá)式 2 為 true 時(shí)執(zhí)行該語句塊
end
end
你可以用同樣的方式嵌套else if...else語句。
實(shí)例:以下實(shí)例用于判斷變量a和b的值。
a = 100;
b = 200;
if( a == 100 ) then
if( b == 200 ) then
print("a 的值為 100 b 的值為 200");
end
end
print("a 的值為 :"..a);
print("b 的值為 :"..b);
--[[
輸出結(jié)果:
a 的值為 100 b 的值為 200
a 的值為 :100
b 的值為 :200
--]]
Lua函數(shù)
在Lua中,函數(shù)是對(duì)語句和表達(dá)式進(jìn)行抽象的主要方法。既可以用來處理一些特殊的工作,也可以用來計(jì)算一些值。
Lua提供了許多的內(nèi)建函數(shù),你可以很方便的在程序中調(diào)用它們,如print()函數(shù)可以將傳入的參數(shù)打印在控制臺(tái)上。
Lua函數(shù)主要有兩種用途:
1.完成指定的任務(wù),這種情況下函數(shù)作為調(diào)用語句使用;2.計(jì)算并返回值,這種情況下函數(shù)作為賦值語句的表達(dá)式使用。
函數(shù)定義?
Lua編程語言函數(shù)定義格式如下:
optional_function_scope function function_name( argument1, argument2, argument3..., argumentn)
function_body
return result_params_comma_separated
end
解析:
optional_function_scope: 該參數(shù)是可選的指定函數(shù)是全局函數(shù)還是局部函數(shù),未設(shè)置該參數(shù)默認(rèn)為全局函數(shù),如果你需要設(shè)置函數(shù)為局部函數(shù)需要使用關(guān)鍵字local。 function_name: 指定函數(shù)名稱。 argument1, argument2, argument3..., argumentn: 函數(shù)參數(shù),多個(gè)參數(shù)以逗號(hào)隔開,函數(shù)也可以不帶參數(shù)。 function_body: 函數(shù)體,函數(shù)中需要執(zhí)行的代碼語句塊。 result_params_comma_separated: 函數(shù)返回值,Lua語言函數(shù)可以返回多個(gè)值,每個(gè)值以逗號(hào)隔開。
?實(shí)例:以下實(shí)例定義了函數(shù) max(),參數(shù)為 num1, num2,用于比較兩值的大小,并返回最大值。
function max(v1,v2)
local result = 0
if(v1 > v2) then
result = v1
else
result = v2
end
return result
end
print("兩值比較,較大值為 "..max(10,5)) --輸出:兩值比較,較大值為 10
?Lua中我們可以將函數(shù)作為參數(shù)傳遞給函數(shù),如下實(shí)例:
myprint = function(param)
print("這是打印函數(shù) - ##",param,"##")
end
function add(num1,num2,functionPrint)
result = num1 + num2
functionPrint(result) --調(diào)用傳遞的函數(shù)參數(shù)
end
myprint(10)
--myprint 函數(shù)作為參數(shù)傳遞
add(2,5,myprint)
--[[
輸出結(jié)果:
這是打印函數(shù) - ## 10 ##
這是打印函數(shù) - ## 7 ##
--]]
多返回值?
Lua函數(shù)可以返回多個(gè)結(jié)果值,比如string.find,其返回匹配串"開始和結(jié)束的下標(biāo)"(如果不存在匹配串返回nil)。
s,e = string.find("This is chinese","chinese")
print(s,e) --輸出:9 15
s,e = string.find("我是中國人","中國")
print(s,e) --輸出:5 8
Lua函數(shù)中,在return后列出要返回的值的列表即可返回多值,如:
function maximum (a)
local k = 1 -- 最大值索引
local m = a[k] -- 最大值
for i,v in ipairs(a) do
if v > m then
k = i
m = v
end
end
return m, k
end
print(maximum({8,10,23,12,5})) --輸出:23 3
可變參數(shù)
Lua函數(shù)可以接受可變數(shù)目的參數(shù),和C語言類似,在函數(shù)參數(shù)列表中使用三點(diǎn) ... 表示函數(shù)有可變的參數(shù)。
function sum(...)
local s = 0
for k,v in ipairs({...}) do
s = s + v
end
return s
end
print("總和 = "..sum(1,2,3,4,5)) --輸出:總和 = 15
我們可以將可變參數(shù)賦值給一個(gè)變量。
例如,我們計(jì)算幾個(gè)數(shù)的平均值:
function average(...)
result = 0
local arg={...} --> arg 為一個(gè)表,局部變量
for k,v in ipairs(arg) do
result = result + v
end
print("總共傳入 " .. #arg .. " 個(gè)數(shù)")
return result/#arg
end
print("平均值為 = "..average(10,5,3,4,5,6))
--[[
輸出結(jié)果:
總共傳入 6 個(gè)數(shù)
平均值為 = 5.5
--]]
我們也可以通過select("#",...)來獲取可變參數(shù)的數(shù)量:
function average(...)
result = 0
local arg={...}
for k,v in ipairs(arg) do
result = result + v
end
print("總共傳入 " .. select("#",...) .. " 個(gè)數(shù)")
return result/select("#",...)
end
print("平均值為 = "..average(10,5,3,4,5,6))
--[[
輸出結(jié)果:
總共傳入 6 個(gè)數(shù)
平均值為 = 5.5
--]]
有時(shí)候我們可能需要幾個(gè)固定參數(shù)加上可變參數(shù),固定參數(shù)必須放在變長參數(shù)之前:
function myFun(str,...)
print(str)
local tab = {...}
for k,v in pairs(tab) do
print(k.." = "..)
end
end
myFun("我是固定參數(shù)1")
myFun("我是固定參數(shù)2",10,20,30)
--[[
輸出結(jié)果:
我是固定參數(shù)1
我是固定參數(shù)2
1 = 10
2 = 20
3 = 30
--]]
通常在遍歷可變參數(shù)的時(shí)候只需要使用 {…},然而可變參數(shù)可能會(huì)包含一些nil,那么就可以用select函數(shù)來訪問可變參數(shù)了:select('#', …)或者select(n, …)
select('#', …)返回可變參數(shù)的長度。select(n, …)用于返回從起點(diǎn)n開始到結(jié)束位置的所有參數(shù)列表。
調(diào)用select時(shí),必須傳入一個(gè)固定實(shí)參selector(選擇開關(guān))和一系列變長參數(shù)。如果selector為數(shù)字 n,那么select返回參數(shù)列表中從索引n開始到結(jié)束位置的所有參數(shù)列表,否則只能為字符串#,這樣select返回變長參數(shù)的總數(shù)。
function myFun(...)
local v = select(3,...)
print(v)
print(select(3,...))
end
myFun(1,2,3,4,5)
--[[
輸出結(jié)果:
3
3 4 5
--]]
function myFun(...)
for i=1,select("#",...) do
print("參數(shù) = "..select(i,...))
end
end
myFun(1,2,3,4,5)
--[[
輸出結(jié)果:
參數(shù) = 1
參數(shù) = 2
參數(shù) = 3
參數(shù) = 4
參數(shù) = 5
--]]
Lua運(yùn)算符
運(yùn)算符是一個(gè)特殊的符號(hào),用于告訴解釋器執(zhí)行特定的數(shù)學(xué)或邏輯運(yùn)算。Lua提供了以下幾種運(yùn)算符類型:
算術(shù)運(yùn)算符關(guān)系運(yùn)算符邏輯運(yùn)算符其他運(yùn)算符
?算術(shù)運(yùn)算符
下表列出了Lua語言中的常用算術(shù)運(yùn)算符,設(shè)定A的值為10,B的值為 20:
操作符描述實(shí)例+加法A + B 輸出結(jié)果 30-減法A - B 輸出結(jié)果 -10*乘法A * B 輸出結(jié)果 200/除法B / A 輸出結(jié)果 2%取余B % A 輸出結(jié)果 0^乘冪A^2 輸出結(jié)果 100-負(fù)號(hào)-A 輸出結(jié)果 -10//整除運(yùn)算符(>=lua5.3)5//2 輸出結(jié)果 2
?在lua中,/用作除法運(yùn)算,計(jì)算結(jié)果包含小數(shù)部分,//用作整除運(yùn)算,計(jì)算結(jié)果不包含小數(shù)部分:
a = 5
b = 2
print("除法運(yùn)算 - a/b 的值為 "..a / b)
print("整除運(yùn)算 - a//b 的值為 "..a // b)
--[[
輸出結(jié)果:
除法運(yùn)算 - a/b 的值為 2.5
整除運(yùn)算 - a//b 的值為 2
--]]
關(guān)系運(yùn)算符?
下表列出了Lua語言中的常用關(guān)系運(yùn)算符,設(shè)定A的值為10,B的值為 20:
操作符描述實(shí)例==等于,檢測兩個(gè)值是否相等,相等返回true,否則返回false(A == B)為false。~=不等于,檢測兩個(gè)值是否相等,不相等返回true,否則返回 false(A ~= B)為 true。>大于,如果左邊的值大于右邊的值,返回true,否則返回false(A > B)為 false。<小于,如果左邊的值大于右邊的值,返回false,否則返回true(A < B)為 true。>=大于等于,如果左邊的值大于等于右邊的值,返回true,否則返回false(A >= B)返回 false。<=小于等于, 如果左邊的值小于等于右邊的值,返回true,否則返回false(A <= B)返回 true。
?邏輯運(yùn)算符
下表列出了Lua語言中的常用邏輯運(yùn)算符,設(shè)定A的值為true,B的值為false:
操作符描述實(shí)例and邏輯與操作符。 若A為false,則返回A,否則返回B。(A and B)為false。or邏輯或操作符。 若A為true,則返回A,否則返回B。(A or B)為true。not邏輯非操作符。與邏輯運(yùn)算結(jié)果相反,如果條件為true,邏輯非為false。not(A and B)true。
?其他運(yùn)算符
下表列出了Lua語言中的連接運(yùn)算符與計(jì)算表或字符串長度的運(yùn)算符:
操作符描述實(shí)例..連接兩個(gè)字符串a(chǎn)..b ,其中a為 "Hello " , b為 "World", 輸出結(jié)果為 "Hello World"。#一元運(yùn)算符,返回字符串或表的長度。#"Hello" 返回 5
運(yùn)算符優(yōu)先級(jí)?
從高到低的順序:
^
not - (unary)
* / %
+ -
..
< > <= >= ~= ==
and
or
除了^和..外所有的二元運(yùn)算符都是左連接的。
a+i < b/2+1 <--> (a+i) < ((b/2)+1)
5+x^2*8 <--> 5+((x^2)*8)
a < y and y <= z <--> (a < y) and (y <= z)
-x^2 <--> -(x^2)
x^y^z <--> x^(y^z)
Lua字符串??
字符串或串(String)是由數(shù)字、字母、下劃線組成的一串字符。
在Lua中,字符串是一種基本的數(shù)據(jù)類型,用于存儲(chǔ)文本數(shù)據(jù)。
Lua中的字符串可以包含任意字符,包括字母、數(shù)字、符號(hào)、空格以及其他特殊字符。
Lua語言中字符串可以使用以下三種方式來表示:
1.單引號(hào)間的一串字符
2.雙引號(hào)間的一串字符
3.[[ 與 ]] 間的一串字符
字符串長度計(jì)算
在Lua中,要計(jì)算字符串的長度(即字符串中字符的個(gè)數(shù)),你可以使用string.len函數(shù)或utf8.len函數(shù),包含中文的一般用utf8.len,string.len函數(shù)用于計(jì)算只包含ASCII字符串的長度。
str1 = "This is chinese"
str2 = "我是中國人"
print(string.len(str1)) --15
print(utf8.len(str2)) --10
以上實(shí)例的str1字符串只包含ASCII字符,因此string.len函數(shù)可以準(zhǔn)確地返回字符串的長度。
包含中文的字符串使用utf8.len函數(shù)。
轉(zhuǎn)義字符用于表示不能直接顯示的字符,比如后退鍵,回車鍵等,如在字符串轉(zhuǎn)換雙引號(hào)可以使用 \。
所有的轉(zhuǎn)義字符和所對(duì)應(yīng)的意義:
轉(zhuǎn)義字符 意義 ASCII碼值(十進(jìn)制) \a 響鈴(BEL) 007 \b 退格(BS) ,將當(dāng)前位置移到前一列 008 \f 換頁(FF),將當(dāng)前位置移到下頁開頭 012 \n 換行(LF) ,將當(dāng)前位置移到下一行開頭 010 \r 回車(CR) ,將當(dāng)前位置移到本行開頭 013 \t 水平制表(HT) (跳到下一個(gè)TAB位置) 009 \v 垂直制表(VT) 011 \\ 代表一個(gè)反斜線字符''\' 092 \' 代表一個(gè)單引號(hào)(撇號(hào))字符 039 \" 代表一個(gè)雙引號(hào)字符 034 \0 空字符(NULL) 000 \ddd 1到3位八進(jìn)制數(shù)所代表的任意字符 三位八進(jìn)制 \xhh 1到2位十六進(jìn)制所代表的任意字符 二位十六進(jìn)制
字符串操作?
Lua提供了很多的方法來支持字符串的操作:
序號(hào)方法 & 用途1string.upper(argument): 字符串全部轉(zhuǎn)為大寫字母。2string.lower(argument): 字符串全部轉(zhuǎn)為小寫字母。3string.gsub(mainString,findString,replaceString,num) 在字符串中替換。 mainString為要操作的字符串,findString為被替換的字符,replaceString要替換的字符,num替換次數(shù)(可以忽略,則全部替換),如: > string.gsub("AAAA","A","B",3);
BBBA????3 4string.find (str, substr, [init, [plain]]) 在一個(gè)指定的目標(biāo)字符串str中搜索指定的內(nèi)容substr,如果找到了一個(gè)匹配的子串,就會(huì)返回這個(gè)子串的起始索引和結(jié)束索引,不存在則返回 nil。 init指定了搜索的起始位置,默認(rèn)為1,可以一個(gè)負(fù)數(shù),表示從后往前數(shù)的字符個(gè)數(shù)。 plain表示是否使用簡單模式,默認(rèn)為false,true只做簡單的查找子串的操作,false表示使用使用正則模式匹配。 以下實(shí)例查找字符串 "Lua" 的起始索引和結(jié)束索引位置: > string.find("Hello Lua user", "Lua", 1)
7????9 5string.reverse(arg) 字符串反轉(zhuǎn) > string.reverse("Lua")
auL 6string.format(...) 返回一個(gè)類似printf的格式化字符串 > string.format("the value is:%d",4)
the value is:4 7string.char(arg) 和 string.byte(arg[,int]) char將整型數(shù)字轉(zhuǎn)成字符并連接, byte轉(zhuǎn)換字符為整數(shù)值(可以指定某個(gè)字符,默認(rèn)第一個(gè)字符)。 > string.char(97,98,99,100)
abcd
> string.byte("ABCD",4)
68
> string.byte("ABCD")
65
> 8string.len(arg) 計(jì)算字符串長度。 string.len("ABC")
3 9string.rep(string, n) 返回字符串string的n個(gè)拷貝 > string.rep("ABCD",2)
ABCDABCD 10.. 鏈接兩個(gè)字符串 > print("www.runoob.".."com")
www.runoob.com 11string.gmatch(str, pattern) 返回一個(gè)迭代器函數(shù),每一次調(diào)用這個(gè)函數(shù),返回一個(gè)在字符串str找到的下一個(gè)符合pattern描述的子串。如果參數(shù)pattern描述的字符串沒有找到,迭代函數(shù)返回nil。 > for word in string.gmatch("Hello Lua user", "%a+") do print(word) end
Hello
Lua
user 12string.match(str, pattern, init) string.match()只尋找源字串str中的第一個(gè)配對(duì),參數(shù)init可選, 指定搜尋過程的起點(diǎn), 默認(rèn)為1。 在成功配對(duì)時(shí), 函數(shù)將返回配對(duì)表達(dá)式中的所有捕獲結(jié)果; 如果沒有設(shè)置捕獲標(biāo)記, 則返回整個(gè)配對(duì)字符串。當(dāng)沒有成功的配對(duì)時(shí), 返回nil。 > = string.match("I have 2 questions for you.", "%d+ %a+")
2 questions
> = string.format("%d, %q", string.match("I have 2 questions for you.", "(%d+) (%a+)"))
2, "questions"
?字符串截取
字符串截取使用sub()方法。
string.sub()用于截取字符串,原型為:
string.sub(s, i [, j])
參數(shù)說明:
s:要截取的字符串。i:截取開始位置。j:截取結(jié)束位置,默認(rèn)為 -1,最后一個(gè)字符。
字符串大小寫轉(zhuǎn)換?
字符串查找與反轉(zhuǎn)
字符串格式化
Lua提供了string.format()函數(shù)來生成具有特定格式的字符串, 函數(shù)的第一個(gè)參數(shù)是格式 , 之后是對(duì)應(yīng)格式中每個(gè)代號(hào)的各種數(shù)據(jù)。
由于格式字符串的存在, 使得產(chǎn)生的長字符串可讀性大大提高了。這個(gè)函數(shù)的格式很像C語言中的 printf()。
以下實(shí)例演示了如何對(duì)字符串進(jìn)行格式化操作:
格式字符串可能包含以下的轉(zhuǎn)義碼:
%c - 接受一個(gè)數(shù)字, 并將其轉(zhuǎn)化為ASCII碼表中對(duì)應(yīng)的字符%d, %i - 接受一個(gè)數(shù)字并將其轉(zhuǎn)化為有符號(hào)的整數(shù)格式%o - 接受一個(gè)數(shù)字并將其轉(zhuǎn)化為八進(jìn)制數(shù)格式%u - 接受一個(gè)數(shù)字并將其轉(zhuǎn)化為無符號(hào)整數(shù)格式%x - 接受一個(gè)數(shù)字并將其轉(zhuǎn)化為十六進(jìn)制數(shù)格式, 使用小寫字母%X - 接受一個(gè)數(shù)字并將其轉(zhuǎn)化為十六進(jìn)制數(shù)格式, 使用大寫字母%e - 接受一個(gè)數(shù)字并將其轉(zhuǎn)化為科學(xué)記數(shù)法格式, 使用小寫字母e%E - 接受一個(gè)數(shù)字并將其轉(zhuǎn)化為科學(xué)記數(shù)法格式, 使用大寫字母E%f - 接受一個(gè)數(shù)字并將其轉(zhuǎn)化為浮點(diǎn)數(shù)格式%g(%G) - 接受一個(gè)數(shù)字并將其轉(zhuǎn)化為%e(%E, 對(duì)應(yīng)%G)及%f中較短的一種格式%q - 接受一個(gè)字符串并將其轉(zhuǎn)化為可安全被Lua編譯器讀入的格式%s - 接受一個(gè)字符串并按照給定的參數(shù)格式化該字符串
為進(jìn)一步細(xì)化格式, 可以在%號(hào)后添加參數(shù). 參數(shù)將以如下的順序讀入:
(1) 符號(hào): 一個(gè)+號(hào)表示其后的數(shù)字轉(zhuǎn)義符將讓正數(shù)顯示正號(hào). 默認(rèn)情況下只有負(fù)數(shù)顯示符號(hào).(2) 占位符: 一個(gè)0, 在后面指定了字串寬度時(shí)占位用. 不填時(shí)的默認(rèn)占位符是空格.(3) 對(duì)齊標(biāo)識(shí): 在指定了字串寬度時(shí), 默認(rèn)為右對(duì)齊, 增加-號(hào)可以改為左對(duì)齊.(4) 寬度數(shù)值(5) 小數(shù)位數(shù)/字串裁切: 在寬度數(shù)值后增加的小數(shù)部分n, 若后接f(浮點(diǎn)數(shù)轉(zhuǎn)義符, 如%6.3f)則設(shè)定該浮點(diǎn)數(shù)的小數(shù)只保留n位, 若后接s(字符串轉(zhuǎn)義符, 如%5.3s)則設(shè)定該字符串只顯示前n位.
string1 = "Lua"
string2 = "Tutorial"
number1 = 10
number2 = 20
-- 基本字符串格式化
print(string.format("基本格式化 %s %s",string1,string2))
-- 日期格式化
date = 2; month = 1; year = 2014
print(string.format("日期格式化 %02d/%02d/%03d", date, month, year))
-- 十進(jìn)制格式化
print(string.format("%.4f",1/3))
--[[
輸出結(jié)果:
基本格式化 Lua Tutorial
日期格式化 02/01/2014
0.3333
--]]
string.format("%c", 83) -- 輸出S
string.format("%+d", 17.0) -- 輸出+17
string.format("%05d", 17) -- 輸出00017
string.format("%o", 17) -- 輸出21
string.format("%u", 3.14) -- 輸出3
string.format("%x", 13) -- 輸出d
string.format("%X", 13) -- 輸出D
string.format("%e", 1000) -- 輸出1.000000e+03
string.format("%E", 1000) -- 輸出1.000000E+03
string.format("%6.3f", 13) -- 輸出13.000
string.format("%q", "One\nTwo") -- 輸出"One\
-- Two"
string.format("%s", "monkey") -- 輸出monkey
string.format("%10s", "monkey") -- 輸出 monkey
string.format("%5.3s", "monkey") -- 輸出 mon
字符與整數(shù)相互轉(zhuǎn)換
?以下實(shí)例演示了字符與整數(shù)相互轉(zhuǎn)換:
-- 字符轉(zhuǎn)換
-- 轉(zhuǎn)換第一個(gè)字符
print(string.byte("Lua"))
-- 轉(zhuǎn)換第三個(gè)字符
print(string.byte("Lua",3))
-- 轉(zhuǎn)換末尾第一個(gè)字符
print(string.byte("Lua",-1))
-- 第二個(gè)字符
print(string.byte("Lua",2))
-- 轉(zhuǎn)換末尾第二個(gè)字符
print(string.byte("Lua",-2))
-- 整數(shù) ASCII 碼轉(zhuǎn)換為字符
print(string.char(97))
--[[
輸出結(jié)果:
76
97
97
117
117
a
--]]
?其他常用函數(shù)
以下實(shí)例演示了其他字符串操作,如計(jì)算字符串長度,字符串連接,字符串復(fù)制等:
string1 = "www."
string2 = "runoob"
string3 = ".com"
-- 使用 .. 進(jìn)行字符串連接
print("連接字符串",string1..string2..string3)
-- 字符串長度
print("字符串長度 ",string.len(string2))
-- 字符串復(fù)制 2 次
repeatedString = string.rep(string2,2)
print(repeatedString)
--[[
輸出結(jié)果:
連接字符串 www.runoob.com
字符串長度 6
runoobrunoob
--]]
匹配模式
Lua中的匹配模式直接用常規(guī)的字符串來描述。 它用于模式匹配函數(shù)string.find, string.gmatch, string.gsub, string.match。
你還可以在模式串中使用字符類。
字符類指可以匹配一個(gè)特定字符集合內(nèi)任何字符的模式項(xiàng)。比如,字符類%d匹配任意數(shù)字。所以你可以使用模式串%d%d/%d%d/%d%d%d%d搜索dd/mm/yyyy格式的日期:
s = "Deadline is 30/05/1999, firm"
date = "%d%d/%d%d/%d%d%d%d"
print(string.sub(s, string.find(s, date))) --> 30/05/1999
下面的表列出了Lua支持的所有字符類:
單個(gè)字符(除 ^$()%.[]*+-? 外): 與該字符自身配對(duì)
.(點(diǎn)): 與任何字符配對(duì)%a: 與任何字母配對(duì)%c: 與任何控制符配對(duì)(例如\n)%d: 與任何數(shù)字配對(duì)%l: 與任何小寫字母配對(duì)%p: 與任何標(biāo)點(diǎn)(punctuation)配對(duì)%s: 與空白字符配對(duì)%u: 與任何大寫字母配對(duì)%w: 與任何字母/數(shù)字配對(duì)%x: 與任何十六進(jìn)制數(shù)配對(duì)%z: 與任何代表0的字符配對(duì)%x(此處x是非字母非數(shù)字字符): 與字符x配對(duì). 主要用來處理表達(dá)式中有功能的字符(^$()%.[]*+-?)的配對(duì)問題, 例如%%與%配對(duì)[數(shù)個(gè)字符類]: 與任何[]中包含的字符類配對(duì). 例如[%w_]與任何字母/數(shù)字, 或下劃線符號(hào)(_)配對(duì)[^數(shù)個(gè)字符類]: 與任何不包含在[]中的字符類配對(duì). 例如[^%s]與任何非空白字符配對(duì)
當(dāng)上述的字符類用大寫書寫時(shí), 表示與非此字符類的任何字符配對(duì). 例如, %S表示與任何非空白字符配對(duì).例如,'%A'非字母的字符:
print(string.gsub("hello, up-down!", "%A", "."))
--輸出:hello..up.down. 4
數(shù)字4不是字符串結(jié)果的一部分,他是gsub返回的第二個(gè)結(jié)果,代表發(fā)生替換的次數(shù)。
在模式匹配中有一些特殊字符,他們有特殊的意義,Lua中的特殊字符如下:
( ) . % + - * ? [ ^ $
'%' 用作特殊字符的轉(zhuǎn)義字符,因此 '%.' 匹配點(diǎn);'%%' 匹配字符 '%'。轉(zhuǎn)義字符 '%'不僅可以用來轉(zhuǎn)義特殊字符,還可以用于所有的非字母的字符。
模式條目可以是:
單個(gè)字符類匹配該類別中任意單個(gè)字符;單個(gè)字符類跟一個(gè) '*', 將匹配零或多個(gè)該類的字符。 這個(gè)條目總是匹配盡可能長的串;單個(gè)字符類跟一個(gè) '+', 將匹配一或更多個(gè)該類的字符。 這個(gè)條目總是匹配盡可能長的串;單個(gè)字符類跟一個(gè) '-', 將匹配零或更多個(gè)該類的字符。 和 '*' 不同, 這個(gè)條目總是匹配盡可能短的串;單個(gè)字符類跟一個(gè) '?', 將匹配零或一個(gè)該類的字符。 只要有可能,它會(huì)匹配一個(gè);%n, 這里的 n 可以從 1 到 9; 這個(gè)條目匹配一個(gè)等于 n 號(hào)捕獲物(后面有描述)的子串。%bxy, 這里的 x 和 y 是兩個(gè)明確的字符; 這個(gè)條目匹配以 x 開始 y 結(jié)束, 且其中 x 和 y 保持 平衡 的字符串。 意思是,如果從左到右讀這個(gè)字符串,對(duì)每次讀到一個(gè) x 就 +1 ,讀到一個(gè) y 就 -1, 最終結(jié)束處的那個(gè) y 是第一個(gè)記數(shù)到 0 的 y。 舉個(gè)例子,條目 %b() 可以匹配到括號(hào)平衡的表達(dá)式。%f[set], 指 邊境模式; 這個(gè)條目會(huì)匹配到一個(gè)位于 set 內(nèi)某個(gè)字符之前的一個(gè)空串, 且這個(gè)位置的前一個(gè)字符不屬于 set 。 集合 set 的含義如前面所述。 匹配出的那個(gè)空串之開始和結(jié)束點(diǎn)的計(jì)算就看成該處有個(gè)字符 '\0' 一樣。
模式:
模式 指一個(gè)模式條目的序列。 在模式最前面加上符號(hào) '^' 將錨定從字符串的開始處做匹配。 在模式最后面加上符號(hào) '$' 將使匹配過程錨定到字符串的結(jié)尾。 如果 '^' 和 '$' 出現(xiàn)在其它位置,它們均沒有特殊含義,只表示自身。
捕獲:
模式可以在內(nèi)部用小括號(hào)括起一個(gè)子模式; 這些子模式被稱為 捕獲物。 當(dāng)匹配成功時(shí),由 捕獲物 匹配到的字符串中的子串被保存起來用于未來的用途。 捕獲物以它們左括號(hào)的次序來編號(hào)。 例如,對(duì)于模式 "(a*(.)%w(%s*))" , 字符串中匹配到 "a*(.)%w(%s*)" 的部分保存在第一個(gè)捕獲物中 (因此是編號(hào) 1); 由 "." 匹配到的字符是 2 號(hào)捕獲物, 匹配到 "%s*" 的那部分是 3 號(hào)。
作為一個(gè)特例,空的捕獲 () 將捕獲到當(dāng)前字符串的位置(它是一個(gè)數(shù)字)。 例如,如果將模式 "()aa()" 作用到字符串 "flaaap" 上,將產(chǎn)生兩個(gè)捕獲物: 3 和 5 。
Lua數(shù)組
數(shù)組,就是相同數(shù)據(jù)類型的元素按一定順序排列的集合,可以是一維數(shù)組和多維數(shù)組。
在Lua中,數(shù)組不是一種特定的數(shù)據(jù)類型,而是一種用來存儲(chǔ)一組值的數(shù)據(jù)結(jié)構(gòu)。
實(shí)際上,Lua中并沒有專門的數(shù)組類型,而是使用一種被稱為 "table" 的數(shù)據(jù)結(jié)構(gòu)來實(shí)現(xiàn)數(shù)組的功能。
Lua數(shù)組的索引鍵值可以使用整數(shù)表示,數(shù)組的大小不是固定的。
在Lua索引值是以1為起始,但你也可以指定0開始。
一維數(shù)組
一維數(shù)組是最簡單的數(shù)組,其邏輯結(jié)構(gòu)是線性表。
獲取數(shù)組中的元素個(gè)數(shù):使用#操作符
向數(shù)組中添加元素:myTable[#myTable+1] = 值
刪除數(shù)組中的元素:table.remove(myTable,index)
myTable = {10,20,30,40,50}
for k,v in ipairs(myTable) do
print(k.." = "..v)
end
print("數(shù)組元素個(gè)數(shù):"..#myTable) --打印數(shù)組元素個(gè)數(shù)
myTable[#myTable+1] = 100 --添加元素
for k,v in ipairs(myTable) do
print(k.." = "..v)
end
table.remove(myTable,2) --刪除元素
for k,v in ipairs(myTable) do
print(k.." = "..v)
end
?注意:使用#操作符來獲取帶有非整數(shù)鍵的數(shù)組時(shí),會(huì)自動(dòng)忽略掉這些鍵值對(duì)。例如
myTable = {1,2,3,key = 4,5}
print(#myTable) --輸出4
多維數(shù)組
多維數(shù)組即數(shù)組中包含數(shù)組或一維數(shù)組的索引鍵對(duì)應(yīng)一個(gè)數(shù)組。
以下是一個(gè)三行三列的陣列多維數(shù)組:
--初始化數(shù)組
array = {}
for i=1,3 do
array[i] = {}
for j=1,3 do
array[i][j] = i*j
end
end
--訪問數(shù)組
for i=1,3 do
for j=1,3 do
print(array[i][j])
end
end
?
?不同索引鍵的三行三列陣列多維數(shù)組:
--初始化數(shù)組
array = {}
maxRows = 3
maxColumns = 3
for row=1,maxRows do
for col=1,maxColumns do
array[row*maxColumns +col] = row*col
end
end
--訪問數(shù)組
for row=1,maxRows do
for col=1,maxColumns do
print(array[row*maxColumns +col])
end
end
Lua迭代器
迭代器(iterator)是一種對(duì)象,它能夠用來遍歷標(biāo)準(zhǔn)模板庫容器中的部分或全部元素,每個(gè)迭代器對(duì)象代表容器中的確定的地址。
在Lua中迭代器是一種支持指針類型的結(jié)構(gòu),它可以遍歷集合的每一個(gè)元素。
泛型for迭代器
泛型for在自己內(nèi)部保存迭代函數(shù),實(shí)際上它保存三個(gè)值:迭代函數(shù)、狀態(tài)常量、控制變量。
泛型for迭代器提供了集合的key/value對(duì),語法格式如下:
for k,v in pairs(myTable) do
print(k,v)
end
-- 或者
for k,v in ipairs(myTable) do
print(k,v)
end
上面代碼中,k, v為變量列表;pairs(myTable)為表達(dá)式列表。
查看以下實(shí)例 :
array = {"Google", "Runoob"}
for key,value in ipairs(array)
do
print(key, value)
end
--[[
以上代碼執(zhí)行輸出結(jié)果為:
1 Google
2 Runoob
--]]
?
以上實(shí)例中我們使用了Lua默認(rèn)提供的迭代函數(shù)ipairs。
下面我們看看泛型for的執(zhí)行過程:
首先,初始化,計(jì)算in后面表達(dá)式的值,表達(dá)式應(yīng)該返回泛型for需要的三個(gè)值:迭代函數(shù)、狀態(tài)常量、控制變量;與多值賦值一樣,如果表達(dá)式返回的結(jié)果個(gè)數(shù)不足三個(gè)會(huì)自動(dòng)用nil補(bǔ)足,多出部分會(huì)被忽略。第二,將狀態(tài)常量和控制變量作為參數(shù)調(diào)用迭代函數(shù)(注意:對(duì)于for結(jié)構(gòu)來說,狀態(tài)常量沒有用處,僅僅在初始化時(shí)獲取他的值并傳遞給迭代函數(shù))。第三,將迭代函數(shù)返回的值賦給變量列表。第四,如果返回的第一個(gè)值為nil循環(huán)結(jié)束,否則執(zhí)行循環(huán)體。第五,回到第二步再次調(diào)用迭代函數(shù)
在Lua中我們常常使用函數(shù)來描述迭代器,每次調(diào)用該函數(shù)就返回集合的下一個(gè)元素。Lua的迭代器包含以下兩種類型:
無狀態(tài)的迭代器多狀態(tài)的迭代器
?無狀態(tài)的迭代器
無狀態(tài)的迭代器是指不保留任何狀態(tài)的迭代器,因此在循環(huán)中我們可以利用無狀態(tài)迭代器避免創(chuàng)建閉包花費(fèi)額外的代價(jià)。
每一次迭代,迭代函數(shù)都是用兩個(gè)變量(狀態(tài)常量和控制變量)的值作為參數(shù)被調(diào)用,一個(gè)無狀態(tài)的迭代器只利用這兩個(gè)值可以獲取下一個(gè)元素。
這種無狀態(tài)迭代器的典型的簡單的例子是ipairs,它遍歷數(shù)組的每一個(gè)元素,元素的索引需要是數(shù)值。
以下實(shí)例我們使用了一個(gè)簡單的函數(shù)來實(shí)現(xiàn)迭代器,實(shí)現(xiàn)數(shù)字n的平方:
function square(iteratorMaxCount,currentNumber)
if(currentNumber currentNumber = currentNumber+1 return currentNumber, currentNumber*currentNumber end end for k,v in square,3,0 do print(k,v) end --[[ 以上實(shí)例輸出結(jié)果為: 1????1 2????4 3????9 --]] ? 迭代的狀態(tài)包括被遍歷的表(循環(huán)過程中不會(huì)改變的狀態(tài)常量)和當(dāng)前的索引下標(biāo)(控制變量),ipairs 和迭代函數(shù)都很簡單,我們?cè)贚ua 中可以這樣實(shí)現(xiàn): function iter(a, i) i = i + 1 local v = a[i] if v then return i, v end end function ipairs(a) return iter, a, 0 end 當(dāng)Lua調(diào)用ipairs(a)開始循環(huán)時(shí),他獲取三個(gè)值:迭代函數(shù)iter、狀態(tài)常量a、控制變量初始值0;然后Lua調(diào)用iter(a,0)返回1, a[1](除非a[1] = nil);第二次迭代調(diào)用iter(a,1) 返回2, a[2]……直到第一個(gè)nil元素。 多狀態(tài)的迭代器 很多情況下,迭代器需要保存多個(gè)狀態(tài)信息而不是簡單的狀態(tài)常量和控制變量,最簡單的方法是使用閉包,還有一種方法就是將所有的狀態(tài)信息封裝到table內(nèi),將table作為迭代器的狀態(tài)常量,因?yàn)檫@種情況下可以將所有的信息存放在table內(nèi),所以迭代函數(shù)通常不需要第二個(gè)參數(shù)。 以下實(shí)例我們創(chuàng)建了自己的迭代器: array = {"Google", "Runoob"} function elementIterator(collection) local index = 0 local count = #collection -- 閉包函數(shù) return function() index = index + 1 if index <= count then return collection[index] -- 返回迭代器的當(dāng)前元素 end end end for element in elementIterator(array) do print(element) end --[[ 以上實(shí)例輸出結(jié)果為: Google Runoob --]] ? 以上實(shí)例中我們可以看到,elementIterator內(nèi)使用了閉包函數(shù),實(shí)現(xiàn)計(jì)算集合大小并輸出各個(gè)元素。? Lua table(表) table是Lua的一種數(shù)據(jù)結(jié)構(gòu)用來幫助我們創(chuàng)建不同的數(shù)據(jù)類型,如:數(shù)組、字典等。 Lua table使用關(guān)聯(lián)型數(shù)組,你可以用任意類型的值來作數(shù)組的索引,但這個(gè)值不能是nil。 Lua table是不固定大小的,你可以根據(jù)自己需要進(jìn)行擴(kuò)容。 Lua也是通過table來解決模塊(module)、包(package)和對(duì)象(Object)的。例如string.format表示使用"format"來索引table string。 table(表)的構(gòu)造 構(gòu)造器是創(chuàng)建和初始化表的表達(dá)式。表是Lua特有的功能強(qiáng)大的東西。最簡單的構(gòu)造函數(shù)是{},用來創(chuàng)建一個(gè)空表??梢灾苯映跏蓟瘮?shù)組: - 初始化表 mytable = {} -- 指定值 mytable[1]= "Lua" -- 移除引用 mytable = nil -- lua 垃圾回收會(huì)釋放內(nèi)存 當(dāng)我們?yōu)閠able a并設(shè)置元素,然后將a賦值給b,則a與b都指向同一個(gè)內(nèi)存。如果a設(shè)置為nil ,則b同樣能訪問table的元素。如果沒有指定的變量指向a,Lua的垃圾回收機(jī)制會(huì)清理相對(duì)應(yīng)的內(nèi)存。 以下實(shí)例演示了以上的描述情況: -- 簡單的 table mytable = {} print("mytable 的類型是 ",type(mytable)) mytable[1]= "Lua" mytable["wow"] = "修改前" print("mytable 索引為 1 的元素是 ", mytable[1]) print("mytable 索引為 wow 的元素是 ", mytable["wow"]) -- alternatetable和mytable的是指同一個(gè) table alternatetable = mytable print("alternatetable 索引為 1 的元素是 ", alternatetable[1]) print("mytable 索引為 wow 的元素是 ", alternatetable["wow"]) alternatetable["wow"] = "修改后" print("mytable 索引為 wow 的元素是 ", mytable["wow"]) -- 釋放變量 alternatetable = nil print("alternatetable 是 ", alternatetable) -- mytable 仍然可以訪問 print("mytable 索引為 wow 的元素是 ", mytable["wow"]) mytable = nil print("mytable 是 ", mytable) --[[ 以上代碼執(zhí)行結(jié)果為: mytable 的類型是 ????table mytable 索引為 1 的元素是 ????Lua mytable 索引為 wow 的元素是 ????修改前 alternatetable 索引為 1 的元素是 ????Lua mytable 索引為 wow 的元素是 ????修改前 mytable 索引為 wow 的元素是 ????修改后 alternatetable 是 ????nil mytable 索引為 wow 的元素是 ????修改后 mytable 是 ????nil --]] ? ?table操作? 以下列出了table操作常用的方法: 序號(hào)方法 & 用途1table.concat (table [, sep [, start [, end]]]): concat是concatenate(連鎖, 連接)的縮寫. table.concat()函數(shù)列出參數(shù)中指定table的數(shù)組部分從start位置到end位置的所有元素, 元素間以指定的分隔符(sep)隔開。 2table.insert (table, [pos,] value): 在table的數(shù)組部分指定位置(pos)插入值為value的一個(gè)元素. pos參數(shù)可選, 默認(rèn)為數(shù)組部分末尾. 3table.maxn (table) 指定table中所有整數(shù)key值中最大的key值. 如果不存在key值為整數(shù)的元素, 則返回0。(Lua5.2之后該方法已經(jīng)不存在了,本文使用了自定義函數(shù)實(shí)現(xiàn)) 4table.remove (table [, pos]) 返回table數(shù)組部分位于pos位置的元素. 其后的元素會(huì)被前移. pos參數(shù)可選, 默認(rèn)為table長度, 即從最后一個(gè)元素刪起。 5table.sort (table [, comp]) 對(duì)給定的table進(jìn)行升序排序。 ?接下來我們來看下這幾個(gè)方法的實(shí)例。 連接:table.concat() 我們可以使用table.concat()輸出一個(gè)列表中元素連接成的字符串: fruits = {"banana","orange","apple"} -- 返回table連接后的字符串 print("連接后的字符串 ",table.concat(fruits)) -- 指定連接字符 print("連接后的字符串 ",table.concat(fruits,", ")) -- 指定索引來連接table print("連接后的字符串 ",table.concat(fruits,", ", 2,3)) --[[ 執(zhí)行以上代碼輸出結(jié)果為: 連接后的字符串 ????bananaorangeapple 連接后的字符串 ????banana, orange, apple 連接后的字符串 ????orange, apple --]] 插入和移除:insert()、remove() 以下實(shí)例演示了table的插入和移除操作: fruits = {"banana","orange","apple"} -- 在末尾插入 table.insert(fruits,"mango") print("索引為 4 的元素為 ",fruits[4]) -- 在索引為 2 的鍵處插入 table.insert(fruits,2,"grapes") print("索引為 2 的元素為 ",fruits[2]) print("最后一個(gè)元素為 ",fruits[5]) table.remove(fruits) print("移除后最后一個(gè)元素為 ",fruits[5]) --[[ 執(zhí)行以上代碼輸出結(jié)果為: 索引為 4 的元素為 ????mango 索引為 2 的元素為 ????grapes 最后一個(gè)元素為 ????mango 移除后最后一個(gè)元素為 ????nil --]] 排序 :table.sort() 以下實(shí)例演示了table.sort()方法的使用,用于對(duì)table進(jìn)行排序: fruits = {"banana","orange","apple","grapes"} print("排序前") for k,v in ipairs(fruits) do ? ? ? ? print(k,v) end table.sort(fruits) print("排序后") for k,v in ipairs(fruits) do ? ? ? ? print(k,v) end --[[ 執(zhí)行以上代碼輸出結(jié)果為: 排序前 1????banana 2????orange 3????apple 4????grapes 排序后 1????apple 2????banana 3????grapes 4????orange --]] ?最大值:table.maxn() table.maxn在Lua5.2之后該方法已經(jīng)不存在了,我們定義了table_maxn方法來實(shí)現(xiàn)。 以下實(shí)例演示了如何獲取table中的最大值: function table_maxn(t) local mn=nil; for k, v in pairs(t) do if(mn==nil) then mn=v end if mn < v then mn = v end end return mn end tbl = {[1] = 2, [2] = 6, [3] = 34, [26] =5} print("tbl 最大值:", table_maxn(tbl)) print("tbl 長度 ", #tbl) --[[ 執(zhí)行以上代碼輸出結(jié)果為: tbl 最大值:????34 tbl 長度 ????3 --]] ? 注意: 當(dāng)我們獲取table的長度的時(shí)候無論是使用#還是table.getn其都會(huì)在索引中斷的地方停止計(jì)數(shù),而導(dǎo)致無法正確取得table的長度。 可以使用以下方法來代替: function table_leng(t) local leng=0 for k, v in pairs(t) do leng=leng+1 end return leng; end Lua模塊與包 模塊類似于一個(gè)封裝庫,從Lua 5.1開始,Lua加入了標(biāo)準(zhǔn)的模塊管理機(jī)制,可以把一些公用的代碼放在一個(gè)文件里,以API接口的形式在其他地方調(diào)用,有利于代碼的重用和降低代碼耦合度。 Lua的模塊是由變量、函數(shù)等已知元素組成的table,因此創(chuàng)建一個(gè)模塊很簡單,就是創(chuàng)建一個(gè)table,然后把需要導(dǎo)出的常量、函數(shù)放入其中,最后返回這個(gè)table就行。以下為創(chuàng)建自定義模塊module.lua,文件代碼格式如下: -- 文件名為 module.lua -- 定義一個(gè)名為 module 的模塊 module = {} ? -- 定義一個(gè)常量 module.constant = "這是一個(gè)常量" ? -- 定義一個(gè)函數(shù) function module.func1() ? ? io.write("這是一個(gè)公有函數(shù)!\n") end ? local function func2() ? ? print("這是一個(gè)私有函數(shù)!") end ? function module.func3() ? ? func2() end ? return module 因此可以像操作調(diào)用table里的元素那樣來操作調(diào)用模塊里的常量或函數(shù)。 上面的func2聲明為程序塊的局部變量,即表示一個(gè)私有函數(shù),因此是不能從外部訪問模塊里的這個(gè)私有函數(shù),必須通過模塊里的公有函數(shù)來調(diào)用。 require函數(shù) Lua提供了一個(gè)名為require的函數(shù)用來加載模塊。要加載一個(gè)模塊,只需要簡單地調(diào)用就可以了。例如: require("<模塊名>")或者require "<模塊名>" 執(zhí)行require后會(huì)返回一個(gè)由模塊常量或函數(shù)組成的table,并且還會(huì)定義一個(gè)包含該table的全局變量。 -- my_module.lua文件 -- module模塊為上文提到到module.lua require("module") ? print(module.constant) ? module.func3() --[[ 以上代碼執(zhí)行結(jié)果為: 這是一個(gè)常量 這是一個(gè)私有函數(shù)! --]] 或者給加載的模塊定義一個(gè)別名變量,方便調(diào)用: -- my_module2.lua文件 -- module模塊為上文提到到module.lua -- 別名變量 m local m = require("module") ? print(m.constant) ? m.func3() --[[ 以上代碼執(zhí)行結(jié)果為: 這是一個(gè)常量 這是一個(gè)私有函數(shù)! --]] 加載機(jī)制? 對(duì)于自定義的模塊,模塊文件不是放在哪個(gè)文件目錄都行,函數(shù)require有它自己的文件路徑加載策略,它會(huì)嘗試從Lua文件或C程序庫中加載模塊。 require用于搜索Lua文件的路徑是存放在全局變量package.path中,當(dāng)Lua啟動(dòng)后,會(huì)以環(huán)境變量 LUA_PATH的值來初始這個(gè)環(huán)境變量。如果沒有找到該環(huán)境變量,則使用一個(gè)編譯時(shí)定義的默認(rèn)路徑來初始化。 當(dāng)然,如果沒有LUA_PATH這個(gè)環(huán)境變量,也可以自定義設(shè)置,在當(dāng)前用戶根目錄下打開.profile 文件(沒有則創(chuàng)建,打開.bashrc文件也可以),例如把"~/lua/"路徑加入LUA_PATH環(huán)境變量里: #LUA_PATH export LUA_PATH="~/lua/?.lua;;" 文件路徑以";"號(hào)分隔,最后的2個(gè)";;"表示新加的路徑后面加上原來的默認(rèn)路徑。 接著,更新環(huán)境變量參數(shù),使之立即生效。 source ~/.profile 這時(shí)假設(shè)package.path的值是: /Users/dengjoe/lua/?.lua;./?.lua;/usr/local/share/lua/5.1/?.lua;/usr/local/share/lua/5.1/?/init.lua;/usr/local/lib/lua/5.1/?.lua;/usr/local/lib/lua/5.1/?/init.lua 那么調(diào)用require("module")時(shí)就會(huì)嘗試打開以下文件目錄去搜索目標(biāo)。 /Users/dengjoe/lua/module.lua; ./module.lua /usr/local/share/lua/5.1/module.lua /usr/local/share/lua/5.1/module/init.lua /usr/local/lib/lua/5.1/module.lua /usr/local/lib/lua/5.1/module/init.lua 如果找過目標(biāo)文件,則會(huì)調(diào)用package.loadfile來加載模塊。否則,就會(huì)去找C程序庫。 搜索的文件路徑是從全局變量package.cpath獲取,而這個(gè)變量則是通過環(huán)境變量LUA_CPATH來初始。 搜索的策略跟上面的一樣,只不過現(xiàn)在換成搜索的是so或dll類型的文件。如果找得到,那么require就會(huì)通過package.loadlib來加載它。 C包 Lua和C是很容易結(jié)合的,使用C為Lua寫包。 與Lua中寫包不同,C包在使用以前必須首先加載并連接,在大多數(shù)系統(tǒng)中最容易的實(shí)現(xiàn)方式是通過動(dòng)態(tài)連接庫機(jī)制。 Lua在一個(gè)叫l(wèi)oadlib的函數(shù)內(nèi)提供了所有的動(dòng)態(tài)連接的功能。這個(gè)函數(shù)有兩個(gè)參數(shù):庫的絕對(duì)路徑和初始化函數(shù)。所以典型的調(diào)用的例子如下: local path = "/usr/local/lua/lib/libluasocket.so" local f = loadlib(path, "luaopen_socket") loadlib函數(shù)加載指定的庫并且連接到Lua,然而它并不打開庫(也就是說沒有調(diào)用初始化函數(shù)),反之他返回初始化函數(shù)作為Lua的一個(gè)函數(shù),這樣我們就可以直接在Lua中調(diào)用他。 如果加載動(dòng)態(tài)庫或者查找初始化函數(shù)時(shí)出錯(cuò),loadlib將返回nil和錯(cuò)誤信息。我們可以修改前面一段代碼,使其檢測錯(cuò)誤然后調(diào)用初始化函數(shù): local path = "/usr/local/lua/lib/libluasocket.so" -- 或者 path = "C:\\windows\\luasocket.dll",這是 Window 平臺(tái)下 local f = assert(loadlib(path, "luaopen_socket")) f() -- 真正打開庫 一般情況下我們期望二進(jìn)制的發(fā)布庫包含一個(gè)與前面代碼段相似的stub文件,安裝二進(jìn)制庫的時(shí)候可以隨便放在某個(gè)目錄,只需要修改stub文件對(duì)應(yīng)二進(jìn)制庫的實(shí)際路徑即可。 將stub文件所在的目錄加入到LUA_PATH,這樣設(shè)定后就可以使用require函數(shù)加載C庫了。 ?Lua元表(Metatable) 在Lua table中我們可以訪問對(duì)應(yīng)的key來得到value值,但是卻無法對(duì)兩個(gè)table進(jìn)行操作(比如相加)。 因此Lua提供了元表(Metatable),允許我們改變table的行為,每個(gè)行為關(guān)聯(lián)了對(duì)應(yīng)的元方法。 例如,使用元表我們可以定義Lua如何計(jì)算兩個(gè)table的相加操作a+b。 當(dāng)Lua試圖對(duì)兩個(gè)表進(jìn)行相加時(shí),先檢查兩者之一是否有元表,之后檢查是否有一個(gè)叫__add的字段,若找到,則調(diào)用對(duì)應(yīng)的值。__add等即時(shí)字段,其對(duì)應(yīng)的值(往往是一個(gè)函數(shù)或是table)就是"元方法"。 有兩個(gè)很重要的函數(shù)來處理元表: setmetatable(table,metatable): 對(duì)指定table設(shè)置元表(metatable),如果元表(metatable)中存在__metatable鍵值,setmetatable會(huì)失敗。getmetatable(table): 返回對(duì)象的元表(metatable)。 以下實(shí)例演示了如何對(duì)指定的表設(shè)置元表: mytable = {} -- 普通表 mymetatable = {} -- 元表 setmetatable(mytable,mymetatable) -- 把mymetatable設(shè)為mytable的元表 以上代碼也可以直接寫成一行:mytable = setmetatable({},{}) 以下為返回對(duì)象元表:getmetatable(mytable) -- 這會(huì)返回mymetatable __index元方法 這是metatable最常用的鍵。 當(dāng)你通過鍵來訪問table的時(shí)候,如果這個(gè)鍵沒有值,那么Lua就會(huì)尋找該table的metatable(假定有metatable)中的__index鍵。如果__index包含一個(gè)表格,Lua會(huì)在表格中查找相應(yīng)的鍵。 我們可以在使用lua腳本式編程模式查看: myTable = {key1=10,key2=20} newTable = setmetatable({},{__index = myTable}) print(newTable.key1) --輸出10 print(newTable.key3) --輸出nil 如果__index包含一個(gè)函數(shù)的話,Lua就會(huì)調(diào)用那個(gè)函數(shù),table和鍵會(huì)作為參數(shù)傳遞給函數(shù)。 __index元方法查看表中元素是否存在,如果不存在,返回結(jié)果為nil;如果存在則由 __index返回結(jié)果。 mytable = setmetatable({key1 = "value1"}, { __index = function(mytable, key) if key == "key2" then return "metatablevalue" else return nil end end }) print(mytable.key1,mytable.key2) --輸出value1 metatablevalue 實(shí)例解析: mytable表賦值為 {key1 = "value1"}。 mytable設(shè)置了元表,元方法為 __index。 在mytable表中查找key1,如果找到,返回該元素,找不到則繼續(xù)。 在mytable表中查找key2,如果找到,返回metatablevalue,找不到則繼續(xù)。 判斷元表有沒有__index方法,如果__index方法是一個(gè)函數(shù),則調(diào)用該函數(shù)。 元方法中查看是否傳入"key2"鍵的參數(shù)(mytable.key2已設(shè)置),如果傳入"key2"參數(shù)返回"metatablevalue",否則返回mytable對(duì)應(yīng)的鍵值。 我們可以將以上代碼簡單寫成: mytable = setmetatable({key1 = "value1"}, { __index = { key2 = "metatablevalue" } }) print(mytable.key1,mytable.key2) 總結(jié) Lua查找一個(gè)表元素時(shí)的規(guī)則,其實(shí)就是如下 3 個(gè)步驟: 1.在表中查找,如果找到,返回該元素,找不到則繼續(xù)2.判斷該表是否有元表,如果沒有元表,返回nil,有元表則繼續(xù)。3.判斷元表有沒有__index方法,如果__index方法為nil,則返回nil;如果__index方法是一個(gè)表,則重復(fù)1、2、3;如果__index方法是一個(gè)函數(shù),則返回該函數(shù)的返回值。 __newindex元方法? __newindex元方法用來更新表,__index則用來訪問表。 當(dāng)你給表的一個(gè)缺少的索引賦值,解釋器就會(huì)查找__newindex元方法:如果存在則調(diào)用這個(gè)函數(shù)而不進(jìn)行賦值操作。 以下實(shí)例演示了__newindex元方法的應(yīng)用: mymetatable = {} mytable = setmetatable({key1 = "value1"}, { __newindex = mymetatable }) print(mytable.key1) mytable.newkey = "新值2" print(mytable.newkey,mymetatable.newkey) mytable.key1 = "新值1" print(mytable.key1,mymetatable.key1) --[[ 以上實(shí)例執(zhí)行輸出結(jié)果為: value1 nil????新值2 新值1????nil --]] 以上實(shí)例中表設(shè)置了元方法__newindex,在對(duì)新索引鍵(newkey)賦值時(shí)(mytable.newkey = "新值2"),會(huì)調(diào)用元方法,而不進(jìn)行賦值。而如果對(duì)已存在的索引鍵(key1),則會(huì)進(jìn)行賦值,而不調(diào)用元方法__newindex。 以下實(shí)例使用了rawset函數(shù)來更新表: mytable = setmetatable({key1 = "value1"}, { ? ? __newindex = function(mytable, key, value) ? ? ? ? rawset(mytable, key, "\""..value.."\"") ? ? end }) mytable.key1 = "new value" mytable.key2 = 4 print(mytable.key1,mytable.key2) --[[ 以上實(shí)例執(zhí)行輸出結(jié)果為: new value????"4" --]] 為表添加操作符 以下實(shí)例演示了兩表相加操作: -- 計(jì)算表中最大值,table.maxn在Lua5.2以上版本中已無法使用 -- 自定義計(jì)算表中最大鍵值函數(shù) table_maxn,即返回表最大鍵值 function table_maxn(t) ? ? local mn = 0 ? ? for k, v in pairs(t) do ? ? ? ? if mn < k then ? ? ? ? ? ? mn = k ? ? ? ? end ? ? end ? ? return mn end -- 兩表相加操作 mytable = setmetatable({ 1, 2, 3 }, { ? __add = function(mytable, newtable) ? ? for i = 1, table_maxn(newtable) do ? ? ? table.insert(mytable, table_maxn(mytable)+1,newtable[i]) ? ? end ? ? return mytable ? end }) secondtable = {4,5,6} mytable = mytable + secondtable ? ? ? ? for k,v in ipairs(mytable) do print(k,v) end --[[ 以上實(shí)例執(zhí)行輸出結(jié)果為: 1????1 2????2 3????3 4????4 5????5 6????6 --]] __add鍵包含在元表中,并進(jìn)行相加操作。 表中對(duì)應(yīng)的操作列表如下:(注意:__是兩個(gè)下劃線) 模式描述__add對(duì)應(yīng)的運(yùn)算符 '+'.__sub對(duì)應(yīng)的運(yùn)算符 '-'.__mul對(duì)應(yīng)的運(yùn)算符 '*'.__div對(duì)應(yīng)的運(yùn)算符 '/'.__mod對(duì)應(yīng)的運(yùn)算符 '%'.__unm對(duì)應(yīng)的運(yùn)算符 '-'.__concat對(duì)應(yīng)的運(yùn)算符 '..'.__eq對(duì)應(yīng)的運(yùn)算符 '=='.__lt對(duì)應(yīng)的運(yùn)算符 '<'.__le對(duì)應(yīng)的運(yùn)算符 '<='. __call元方法? _call元方法在Lua調(diào)用一個(gè)值時(shí)調(diào)用。以下實(shí)例演示了計(jì)算表中元素的和: -- 計(jì)算表中最大值,table.maxn在Lua5.2以上版本中已無法使用 -- 自定義計(jì)算表中最大鍵值函數(shù)table_maxn,即計(jì)算表的元素個(gè)數(shù) function table_maxn(t) local mn = 0 for k, v in pairs(t) do if mn < k then mn = k end end return mn end -- 定義元方法__call mytable = setmetatable({10}, { __call = function(mytable, newtable) sum = 0 for i = 1, table_maxn(mytable) do sum = sum + mytable[i] end for i = 1, table_maxn(newtable) do sum = sum + newtable[i] end return sum end }) newtable = {10,20,30} print(mytable(newtable)) -- 以上實(shí)例執(zhí)行輸出結(jié)果為:70 __tostring元方法 __tostring元方法用于修改表的輸出行為。以下實(shí)例我們自定義了表的輸出內(nèi)容: mytable = setmetatable({ 10, 20, 30 }, { __tostring = function(mytable) sum = 0 for k, v in pairs(mytable) do sum = sum + v end return "表所有元素的和為 " .. sum end }) print(mytable) -- 以上實(shí)例執(zhí)行輸出結(jié)果為:表所有元素的和為 60 從本文中我們可以知道元表可以很好的簡化我們的代碼功能,所以了解Lua的元表,可以讓我們寫出更加簡單優(yōu)秀的Lua代碼。 Lua協(xié)同程序(coroutine) 什么是協(xié)同(coroutine)? Lua協(xié)同程序(coroutine)與線程比較類似:擁有獨(dú)立的堆棧,獨(dú)立的局部變量,獨(dú)立的指令指針,同時(shí)又與其它協(xié)同程序共享全局變量和其它大部分東西。 協(xié)同程序可以理解為一種特殊的線程,可以暫停和恢復(fù)其執(zhí)行,從而允許非搶占式的多任務(wù)處理。 協(xié)同是非常強(qiáng)大的功能,但是用起來也很復(fù)雜。 基本語法 協(xié)同程序由coroutine模塊提供支持。 使用協(xié)同程序,你可以在函數(shù)中使用coroutine.create創(chuàng)建一個(gè)新的協(xié)同程序?qū)ο螅⑹褂胏oroutine.resume啟動(dòng)它的執(zhí)行。協(xié)同程序可以通過調(diào)用coroutine.yield來主動(dòng)暫停自己的執(zhí)行,并將控制權(quán)交還給調(diào)用者。 方法描述coroutine.create()創(chuàng)建 coroutine,返回 coroutine, 參數(shù)是一個(gè)函數(shù),當(dāng)和 resume 配合使用的時(shí)候就喚醒函數(shù)調(diào)用coroutine.resume()重啟 coroutine,和 create 配合使用coroutine.yield()掛起 coroutine,將 coroutine 設(shè)置為掛起狀態(tài),這個(gè)和 resume 配合使用能有很多有用的效果coroutine.status()查看 coroutine 的狀態(tài) 注:coroutine 的狀態(tài)有三種:dead,suspended,running,具體什么時(shí)候有這樣的狀態(tài)請(qǐng)參考下面的程序coroutine.wrap()創(chuàng)建 coroutine,返回一個(gè)函數(shù),一旦你調(diào)用這個(gè)函數(shù),就進(jìn)入 coroutine,和 create 功能重復(fù)coroutine.running()返回正在跑的 coroutine,一個(gè) coroutine 就是一個(gè)線程,當(dāng)使用running的時(shí)候,就是返回一個(gè) coroutine 的線程號(hào) 以下實(shí)例演示了如何使用Lua協(xié)同程序: function speak() print("協(xié)同程序 speak 開始執(zhí)行") local value = coroutine.yield("掛起協(xié)同程序 speak 暫停執(zhí)行") print("協(xié)同程序 speak 恢復(fù)執(zhí)行,傳入的值為:"..tostring(value)) print("協(xié)同程序 speak 結(jié)束執(zhí)行") end local co = coroutine.create(speak) --創(chuàng)建協(xié)同程序 -- 啟動(dòng)協(xié)同程序 local status,result = coroutine.resume(co) print(status,result) -- 恢復(fù)協(xié)同程序 status,result = coroutine.resume(co,10) print(status,result) --[[ 輸出結(jié)果: 協(xié)同程序 speak 開始執(zhí)行 true 掛起協(xié)同程序 speak 暫停執(zhí)行 協(xié)同程序 speak 恢復(fù)執(zhí)行,傳入的值為:10 協(xié)同程序 speak 結(jié)束執(zhí)行 true nil --]] 以上實(shí)例中,我們定義了一個(gè)名為speak的函數(shù)作為協(xié)同程序。在函數(shù)中,我們使用coroutine.yield暫停了協(xié)同程序的執(zhí)行,并返回了一個(gè)值。在主程序中,我們使用coroutine.create創(chuàng)建了一個(gè)協(xié)同程序?qū)ο?,并使用coroutine.resume啟動(dòng)了它的執(zhí)行。 在第一次調(diào)用coroutine.resume后,協(xié)同程序執(zhí)行到coroutine.yield處暫停,并將值返回給主程序。然后,我們?cè)俅握{(diào)用coroutine.resume,并傳入一個(gè)值作為協(xié)同程序恢復(fù)執(zhí)行時(shí)的參數(shù)。 需要注意的是,協(xié)同程序的狀態(tài)可以通過coroutine.status函數(shù)獲取,通過檢查狀態(tài)可以確定協(xié)同程序的執(zhí)行情況(如運(yùn)行中、已掛起、已結(jié)束等)。 以下實(shí)例演示了以上各個(gè)方法的用法: co1 = coroutine.create( function(value) print("傳入的值為:"..value) end ) coroutine.resume(co1,10) --輸出:傳入的值為:10 print(coroutine.status(co1)) --輸出:dead print("--------------------") -- 使用coroutine.wrap創(chuàng)建了一個(gè)協(xié)同程序包裝器,將協(xié)同程序轉(zhuǎn)換為一個(gè)可直接調(diào)用的函數(shù)對(duì)象。 co1 = coroutine.wrap( function(value) print("傳入的值為:"..value) end ) co1(20) --輸出:傳入的值為:20 print("--------------------") co2 = coroutine.create( function() for i=1,10 do print(i) if i == 3 then print(coroutine.status(co2)) print(coroutine.running()) end coroutine.yield("第"..i.."次,掛起協(xié)同程序") end end ) status,result = coroutine.resume(co2) --輸出:1 print(status,result) --輸出: status,result = coroutine.resume(co2) --輸出:2 print(status,result) --輸出: status,result = coroutine.resume(co2) --輸出:3 print(status,result) --輸出: -- 通過coroutine.status檢查協(xié)同程序co2的狀態(tài),輸出為suspended,表示協(xié)同程序暫停執(zhí)行。 print(coroutine.status(co2)) --輸出:suspended print(coroutine.running()) --輸出:nil --[[ 輸出結(jié)果: 傳入的值為:10 dead -------------------- 傳入的值為:20 -------------------- 1 true 第1次,掛起協(xié)同程序 2 true 第2次,掛起協(xié)同程序 3 running thread: 00AC8628 true 第3次,掛起協(xié)同程序 suspended nil --]] 從coroutine.running就可以看出來,coroutine在底層實(shí)現(xiàn)就是一個(gè)線程。 當(dāng)create一個(gè)coroutine的時(shí)候就是在新線程中注冊(cè)了一個(gè)事件。 當(dāng)使用resume觸發(fā)事件的時(shí)候,create的coroutine函數(shù)就被執(zhí)行了,當(dāng)遇到y(tǒng)ield的時(shí)候就代表掛起當(dāng)前線程,等候再次resume觸發(fā)事件。 詳細(xì)實(shí)例: function speak(value) print("speak 函數(shù)開始執(zhí)行,傳入的值為:"..value) return coroutine.yield(value*2) end co = coroutine.create( function(v1,v2) print("第一次執(zhí)行協(xié)同程序,傳入的值為:"..v1.."和"..v2) local value = speak(v1) print("第二次執(zhí)行協(xié)同程序,傳入的值為:"..value) local r,s = coroutine.yield(v1+v2,v1-v2) print("第三次執(zhí)行協(xié)同程序,傳入的值為:"..r.."和"..s) return v2,"結(jié)束協(xié)同程序" end ) print(coroutine.resume(co, 1, 10)) -- true 2 print("--------------------") print(coroutine.resume(co, "r")) -- true 11 -9 print("--------------------") print(coroutine.resume(co, "x", "y")) -- true 10 結(jié)束協(xié)同程序 print("--------------------") print(coroutine.resume(co, "x", "y")) -- false cannot resume dead coroutine --[[ 輸出結(jié)果: 第一次執(zhí)行協(xié)同程序,傳入的值為:1和10 speak 函數(shù)開始執(zhí)行,傳入的值為:1 true 2 -------------------- 第二次執(zhí)行協(xié)同程序,傳入的值為:r true 11 -9 -------------------- 第三次執(zhí)行協(xié)同程序,傳入的值為:x和y true 10 結(jié)束協(xié)同程序 -------------------- false cannot resume dead coroutine --]] 以上實(shí)例步驟如下: 調(diào)用resume,將協(xié)同程序喚醒,resume操作成功返回true,否則返回false;協(xié)同程序運(yùn)行;運(yùn)行到y(tǒng)ield語句;yield掛起協(xié)同程序,第一次resume返回;(注意:此處yield返回,參數(shù)是resume的參數(shù))第二次resume,再次喚醒協(xié)同程序;(注意:此處resume的參數(shù)中,除了第一個(gè)參數(shù),剩下的參數(shù)將作為yield的參數(shù))yield返回;協(xié)同程序繼續(xù)運(yùn)行;如果使用的協(xié)同程序繼續(xù)運(yùn)行完成后繼續(xù)調(diào)用resume方法則輸出:cannot resume dead coroutine resume和yield的配合強(qiáng)大之處在于,resume處于主程中,它將外部狀態(tài)(數(shù)據(jù))傳入到協(xié)同程序內(nèi)部;而yield則將內(nèi)部的狀態(tài)(數(shù)據(jù))返回到主程中。? 生產(chǎn)者-消費(fèi)者案例 local co_productor function productor() --生產(chǎn)者 local i = 0 while true do i = i + 1 coroutine.yield("物品:"..i) --將生產(chǎn)的物品發(fā)送給消費(fèi)者 end end function consumer(value) --消費(fèi)者 local i = 0 while(i < value) do i = i + 1 local status,result = coroutine.resume(co_productor) --啟動(dòng)協(xié)同程序 print(status,result) end end co_productor = coroutine.create(productor) --創(chuàng)建協(xié)同程序 consumer(10) --消費(fèi)10個(gè)物品 --[[ 輸出結(jié)果: true 物品:1 true 物品:2 true 物品:3 true 物品:4 true 物品:5 true 物品:6 true 物品:7 true 物品:8 true 物品:9 true 物品:10 --]] 線程和協(xié)同程序的區(qū)別 線程與協(xié)同程序的主要區(qū)別在于,一個(gè)具有多個(gè)線程的程序可以同時(shí)運(yùn)行幾個(gè)線程,而協(xié)同程序卻需要彼此協(xié)作的運(yùn)行。 在任一指定時(shí)刻只有一個(gè)協(xié)同程序在運(yùn)行,并且這個(gè)正在運(yùn)行的協(xié)同程序只有在明確的被要求掛起的時(shí)候才會(huì)被掛起。 協(xié)同程序有點(diǎn)類似同步的多線程,在等待同一個(gè)線程鎖的幾個(gè)線程有點(diǎn)類似協(xié)同。 主要區(qū)別歸納如下: 調(diào)度方式:線程通常由操作系統(tǒng)的調(diào)度器進(jìn)行搶占式調(diào)度,操作系統(tǒng)會(huì)在不同線程之間切換執(zhí)行權(quán)。而協(xié)同程序是非搶占式調(diào)度的,它們由程序員顯式地控制執(zhí)行權(quán)的轉(zhuǎn)移。并發(fā)性:線程是并發(fā)執(zhí)行的,多個(gè)線程可以同時(shí)運(yùn)行在多個(gè)處理器核心上,或者通過時(shí)間片輪轉(zhuǎn)在單個(gè)核心上切換執(zhí)行。協(xié)同程序則是協(xié)作式的,只有一個(gè)協(xié)同程序處于運(yùn)行狀態(tài),其他協(xié)同程序必須等待當(dāng)前協(xié)同程序主動(dòng)放棄執(zhí)行權(quán)。內(nèi)存占用:線程通常需要獨(dú)立的堆棧和上下文環(huán)境,因此線程的創(chuàng)建和銷毀會(huì)帶來額外的開銷。而協(xié)同程序可以共享相同的堆棧和上下文,因此創(chuàng)建和銷毀協(xié)同程序的開銷較小。數(shù)據(jù)共享:線程之間可以共享內(nèi)存空間,但需要注意線程安全性和同步問題。協(xié)同程序通常通過參數(shù)傳遞和返回值來進(jìn)行數(shù)據(jù)共享,不同協(xié)同程序之間的數(shù)據(jù)隔離性較好。調(diào)試和錯(cuò)誤處理:線程通常在調(diào)試和錯(cuò)誤處理方面更復(fù)雜,因?yàn)槎鄠€(gè)線程之間的交互和并發(fā)執(zhí)行可能導(dǎo)致難以調(diào)試的問題。協(xié)同程序則在調(diào)試和錯(cuò)誤處理方面相對(duì)簡單,因?yàn)樗鼈兪怯沙绦騿T顯式地控制執(zhí)行流程的。 總體而言,線程適用于需要并發(fā)執(zhí)行的場景,例如在多核處理器上利用并行性加快任務(wù)的執(zhí)行速度。而協(xié)同程序適用于需要協(xié)作和協(xié)調(diào)的場景,例如狀態(tài)機(jī)、事件驅(qū)動(dòng)編程或協(xié)作式任務(wù)處理。選擇使用線程還是協(xié)同程序取決于具體的應(yīng)用需求和編程模型。 Lua文件I/O Lua I/O庫用于讀取和處理文件。分為簡單模式(和C一樣)、完全模式。 簡單模式(simple model)擁有一個(gè)當(dāng)前輸入文件和一個(gè)當(dāng)前輸出文件,并且提供針對(duì)這些文件相關(guān)的操作。完全模式(complete model)使用外部的文件句柄來實(shí)現(xiàn)。它以一種面向?qū)ο蟮男问?,將所有的文件操作定義為文件句柄的方法。 簡單模式在做一些簡單的文件操作時(shí)較為合適。但是在進(jìn)行一些高級(jí)的文件操作的時(shí)候,簡單模式就顯得力不從心。例如同時(shí)讀取多個(gè)文件這樣的操作,使用完全模式則較為合適。 打開文件操作語句如下: file = io.open(filename[,mode]) mode的值有: r:以只讀方式打開文件,該文件必須存在。 w:打開只寫文件,若文件存在則文件長度清為0,即該文件內(nèi)容會(huì)消失。若文件不存在則建立該文件。 a:以附加的方式打開只寫文件。若文件不存在,則會(huì)建立該文件,如果文件存在,寫入的數(shù)據(jù)會(huì)被加到文件尾,即文件原先的內(nèi)容會(huì)被保留。(EOF符保留) r+:以可讀寫方式打開文件,該文件必須存在。 w+:打開可讀寫文件,若文件存在則文件長度清為0,即該文件內(nèi)容會(huì)消失。若文件不存在則建立該文件。 a+:以附加的方式打開可讀寫文件。若文件不存在,則會(huì)建立該文件,如果文件存在,寫入的數(shù)據(jù)會(huì)被加到文件尾,即文件原先的內(nèi)容會(huì)被保留。(EOF符保留) b:二進(jìn)制模式(如果文件是二進(jìn)制文件)。 +:表示對(duì)文件既可以讀也可以寫。 簡單模式 簡單模式使用標(biāo)準(zhǔn)的I/O或使用一個(gè)當(dāng)前輸入文件和一個(gè)當(dāng)前輸出文件。 以下為 LuaStudy.lua 文件代碼,操作的文件為file.lua(如果沒有需要?jiǎng)?chuàng)建該文件),代碼如下: file = io.open("file.lua","r") -- 以只讀方式打開文件 io.input(file) -- 設(shè)置默認(rèn)輸入文件為 file.lua print(io.read()) -- 輸出文件第一行 io.close(file) -- 關(guān)閉打開的文件 file = io.open("file.lua","a") -- 以附加的方式打開只寫文件 io.output(file) -- 設(shè)置默認(rèn)輸出文件為 file.lua io.write("--新添加的內(nèi)容") -- 在文件最后一行添加 Lua 注釋 io.close() -- 關(guān)閉打開的文件 執(zhí)行完以上代碼后,file.lua的文件內(nèi)容為: --大家好啊 --新添加的內(nèi)容 在以上實(shí)例中我們使用了io."x"方法,其中io.read()中我們沒有帶參數(shù),參數(shù)可以是下表中的一個(gè): 模式描述"*n"讀取一個(gè)數(shù)字并返回它。例:file.read("*n")"*a"從當(dāng)前位置讀取整個(gè)文件。例:file.read("*a")"*l"(默認(rèn))讀取下一行,在文件尾(EOF)處返回nil。例:file.read("*l")number返回一個(gè)指定字符個(gè)數(shù)的字符串,或在EOF時(shí)返回nil。例:file.read(5) 其他的io方法有: io.tmpfile():返回一個(gè)臨時(shí)文件句柄,該文件以更新模式打開,程序結(jié)束時(shí)自動(dòng)刪除。 io.type(file): 檢測obj是否一個(gè)可用的文件句柄。 io.flush(): 向文件寫入緩沖中的所有數(shù)據(jù)。 io.lines(optional file name): 返回一個(gè)迭代函數(shù),每次調(diào)用將獲得文件中的一行內(nèi)容,當(dāng)?shù)轿募矔r(shí),將返回nil,但不關(guān)閉文件。 完全模式 通常我們需要在同一時(shí)間處理多個(gè)文件。我們需要使用file:function_name來代替io.function_name方法。以下實(shí)例演示了如何同時(shí)處理同一個(gè)文件: file = io.open("file.lua","r") -- 以只讀方式打開文件 print(file:read()) -- 輸出文件第一行 file:close() -- 關(guān)閉打開的文件 file = io.open("file.lua","a") -- 以附加的方式打開只寫文件 file:write("--又添加了新的文件內(nèi)容") - 在文件最后一行添加 Lua 注釋 file:close() -- 關(guān)閉打開的文件 執(zhí)行完以上代碼后,file.lua的文件內(nèi)容為: --大家好啊 --新添加的內(nèi)容 --又添加了新的文件內(nèi)容 read的參數(shù)與簡單模式一致。 其他方法: file:seek(optional whence, optional offset): 設(shè)置和獲取當(dāng)前文件位置,成功則返回最終的文件位置(按字節(jié)),失敗則返回nil加錯(cuò)誤信息。參數(shù)whence值可以是: "set":從文件頭開始"cur":從當(dāng)前位置開始[默認(rèn)]"end":從文件尾開始o(jì)ffset:默認(rèn)為0 不帶參數(shù)file:seek()則返回當(dāng)前位置,file:seek("set")則定位到文件頭,file:seek("end")則定位到文件尾并返回文件大小 file:flush(): 向文件寫入緩沖中的所有數(shù)據(jù) io.lines(optional file name): 打開指定的文件filename為讀模式并返回一個(gè)迭代函數(shù),每次調(diào)用將獲得文件中的一行內(nèi)容,當(dāng)?shù)轿募矔r(shí),將返回nil,并自動(dòng)關(guān)閉文件。 若不帶參數(shù)時(shí)io.lines() <=> io.input():lines(); 讀取默認(rèn)輸入設(shè)備的內(nèi)容,但結(jié)束時(shí)不關(guān)閉文件,如: for line in io.lines("file.lua") do print(line) end ?以下實(shí)例使用了seek方法,定位到文件倒數(shù)第15個(gè)位置并使用read方法的*a參數(shù),即從當(dāng)前位置(倒數(shù)第15個(gè)位置)讀取整個(gè)文件。 file = io.open("file.lua", "r") -- 以只讀方式打開文件 file:seek("end",-15) print(file:read("*a")) file:close() -- 關(guān)閉打開的文件 Lua錯(cuò)誤處理 程序運(yùn)行中錯(cuò)誤處理是必要的,在我們進(jìn)行文件操作,數(shù)據(jù)轉(zhuǎn)移及web service調(diào)用過程中都會(huì)出現(xiàn)不可預(yù)期的錯(cuò)誤。如果不注重錯(cuò)誤信息的處理,就會(huì)造成信息泄露,程序無法運(yùn)行等情況。 任何程序語言中,都需要錯(cuò)誤處理。錯(cuò)誤類型有: 語法錯(cuò)誤運(yùn)行錯(cuò)誤 語法錯(cuò)誤 運(yùn)行錯(cuò)誤 錯(cuò)誤處理 我們可以使用兩個(gè)函數(shù):assert和error來處理錯(cuò)誤。實(shí)例如下: local function add(a,b) assert(type(a) == "number", "a 不是一個(gè)數(shù)字") assert(type(b) == "number", "b 不是一個(gè)數(shù)字") return a+b end add(10) --[[ 輸出結(jié)果: lua: LuaStudy.lua:356: b 不是一個(gè)數(shù)字 stack traceback: [C]: in function 'assert' LuaStudy.lua:356: in function 'add' LuaStudy.lua:359: in main chunk [C]: ? --]] 實(shí)例中assert首先檢查第一個(gè)參數(shù),若沒問題,assert不做任何事情;否則,assert以第二個(gè)參數(shù)作為錯(cuò)誤信息拋出。 error函數(shù) 語法格式: error (message [, level]) 功能:終止正在執(zhí)行的函數(shù),并返回message的內(nèi)容作為錯(cuò)誤信息(error函數(shù)永遠(yuǎn)都不會(huì)返回)。 通常情況下,error會(huì)附加一些錯(cuò)誤位置的信息到message頭部。 Level參數(shù)指示獲得錯(cuò)誤的位置: Level=1[默認(rèn)]:為調(diào)用error位置(文件+行號(hào))Level=2:指出哪個(gè)調(diào)用error的函數(shù)的函數(shù)Level=0:不添加錯(cuò)誤位置信息 pcall 和 xpcall、debug Lua中處理錯(cuò)誤,可以使用函數(shù)pcall(protected call)來包裝需要執(zhí)行的代碼。 pcall接收一個(gè)函數(shù)和要傳遞給后者的參數(shù),并執(zhí)行,執(zhí)行結(jié)果:有錯(cuò)誤、無錯(cuò)誤;返回值true或者或false、errorinfo。 語法格式: if pcall(function_name,....) then -- 沒有錯(cuò)誤 else -- 一些錯(cuò)誤 end print(pcall(function(i) print(i) end, 33)) print(pcall(function(i) print(i) error('error..') end, 33)) --[[ 輸出結(jié)果: 33 true 33 false LuaStudy.lua:362: error.. --]] function f() return false,2 end if f() then print '1' else print '0' end --輸出:0 pcall以一種"保護(hù)模式"來調(diào)用第一個(gè)參數(shù),因此pcall可以捕獲函數(shù)執(zhí)行中的任何錯(cuò)誤。 通常在錯(cuò)誤發(fā)生時(shí),希望得到更多的調(diào)試信息,而不只是發(fā)生錯(cuò)誤的位置。但pcall返回時(shí),它已經(jīng)銷毀了調(diào)用桟的部分內(nèi)容。 Lua提供了xpcall函數(shù),xpcall接收第二個(gè)參數(shù)(一個(gè)錯(cuò)誤處理函數(shù)),當(dāng)錯(cuò)誤發(fā)生時(shí),Lua會(huì)在調(diào)用桟展開(unwind)前調(diào)用錯(cuò)誤處理函數(shù),于是就可以在這個(gè)函數(shù)中使用debug庫來獲取關(guān)于錯(cuò)誤的額外信息了。 debug庫提供了兩個(gè)通用的錯(cuò)誤處理函數(shù): debug.debug:提供一個(gè)Lua提示符,讓用戶來檢查錯(cuò)誤的原因。debug.traceback:根據(jù)調(diào)用桟來構(gòu)建一個(gè)擴(kuò)展的錯(cuò)誤消息。 print(xpcall(function(i) print(i) error('error..') end, function() print(debug.traceback()) end, 33)) --[[ 輸出結(jié)果: nil stack traceback: LuaStudy.lua:367: in function [C]: in function 'error' LuaStudy.lua:367: in function [C]: in function 'xpcall' LuaStudy.lua:367: in main chunk [C]: ? false nil --]] function myfunction() n = n/nil end function myerrorhandler(err) print( "ERROR:", err ) end status = xpcall(myfunction, myerrorhandler) print(status) --[[ 輸出結(jié)果: ERROR: LuaStudy.lua:370: attempt to perform arithmetic on global 'n' (a nil value) false --]] Lua調(diào)試(Debug) Lua提供了debug庫用于提供創(chuàng)建我們自定義調(diào)試器的功能。Lua本身并未有內(nèi)置的調(diào)試器,但很多開發(fā)者共享了他們的Lua調(diào)試器代碼。 Lua中debug庫包含以下函數(shù): 序號(hào)方法 & 用途1debug(): 進(jìn)入一個(gè)用戶交互模式,運(yùn)行用戶輸入的每個(gè)字符串。 使用簡單的命令以及其它調(diào)試設(shè)置,用戶可以檢閱全局變量和局部變量, 改變變量的值,計(jì)算一些表達(dá)式,等等。 輸入一行僅包含cont的字符串將結(jié)束這個(gè)函數(shù), 這樣調(diào)用者就可以繼續(xù)向下運(yùn)行。 2getfenv(object): 返回對(duì)象的環(huán)境變量。 3gethook(optional thread): 返回三個(gè)表示線程鉤子設(shè)置的值: 當(dāng)前鉤子函數(shù),當(dāng)前鉤子掩碼,當(dāng)前鉤子計(jì)數(shù) 4getinfo([thread,] f [, what]): 返回關(guān)于一個(gè)函數(shù)信息的表。 你可以直接提供該函數(shù), 也可以用一個(gè)數(shù)字f表示該函數(shù)。 數(shù)字f表示運(yùn)行在指定線程的調(diào)用棧對(duì)應(yīng)層次上的函數(shù): 0層表示當(dāng)前函數(shù)(getinfo 自身); 1 層表示調(diào)用getinfo的函數(shù)(除非是尾調(diào)用,這種情況不計(jì)入棧);等等。 如果f是一個(gè)比活動(dòng)函數(shù)數(shù)量還大的數(shù)字,getinfo返回nil。 5debug.getlocal([thread,] f, local): 此函數(shù)返回在棧的f層處函數(shù)的索引為local的局部變量的名字和值。 這個(gè)函數(shù)不僅用于訪問顯式定義的局部變量,也包括形參、臨時(shí)變量等。 6getmetatable(value): 把給定索引指向的值的元表壓入堆棧。如果索引無效,或是這個(gè)值沒有元表,函數(shù)將返回0并且不會(huì)向棧上壓任何東西。 7getregistry(): 返回注冊(cè)表,這是一個(gè)預(yù)定義出來的表, 可以用來保存任何C代碼想保存的Lua值。 8getupvalue(f, up) 此函數(shù)返回函數(shù)f的第up個(gè)上值的名字和值。 如果該函數(shù)沒有那個(gè)上值,返回nil 。 以 '(' (開括號(hào))打頭的變量名表示沒有名字的變量(去除了調(diào)試信息的代碼塊)。 10sethook([thread,] hook, mask [, count]): 將一個(gè)函數(shù)作為鉤子函數(shù)設(shè)入。 字符串mask以及數(shù)字count決定了鉤子將在何時(shí)調(diào)用。 掩碼是由下列字符組合成的字符串,每個(gè)字符有其含義: 'c': 每當(dāng)Lua調(diào)用一個(gè)函數(shù)時(shí),調(diào)用鉤子。'r': 每當(dāng)Lua從一個(gè)函數(shù)內(nèi)返回時(shí),調(diào)用鉤子。'l': 每當(dāng)Lua進(jìn)入新的一行時(shí),調(diào)用鉤子。11setlocal([thread,] level, local, value): 這個(gè)函數(shù)將value賦給 棧上第level層函數(shù)的第local個(gè)局部變量。 如果沒有那個(gè)變量,函數(shù)返回nil 。 如果level越界,拋出一個(gè)錯(cuò)誤。 12setmetatable(value, table): 將value的元表設(shè)為table(可以是 nil)。 返回 value。 13setupvalue(f, up, value): 這個(gè)函數(shù)將value設(shè)為函數(shù)f的第up個(gè)上值。 如果函數(shù)沒有那個(gè)上值,返回nil否則,返回該上值的名字。 14traceback([thread,] [message [, level]]): 如果message有,且不是字符串或nil, 函數(shù)不做任何處理直接返回message。 否則,它返回調(diào)用棧的?;厮菪畔?。 字符串可選項(xiàng)message被添加在?;厮菪畔⒌拈_頭。 數(shù)字可選項(xiàng)level指明從棧的哪一層開始回溯 (默認(rèn)為 1 ,即調(diào)用traceback的那里)。 function myfunction() print(debug.traceback("Stack trace")) print(debug.getinfo(1)) print("Stack trace end") return 10 end myfunction() print(debug.getinfo(1)) --[[ 輸出結(jié)果: Stack trace stack traceback: LuaStudy.lua:381: in function 'myfunction' LuaStudy.lua:386: in main chunk [C]: ? table: 00BF9820 Stack trace end table: 00BF9A50 --]] 在以實(shí)例中,我們使用到了debug庫的traceback和getinfo函數(shù),getinfo函數(shù)用于返回函數(shù)信息的表。 我們經(jīng)常需要調(diào)試函數(shù)的內(nèi)的局部變量。我們可以使用setupvalue函數(shù)來設(shè)置這些局部變量。實(shí)例如下: function newCounter () local n = 0 local k = 0 return function () k = n n = n + 1 return n end end counter = newCounter () print(counter()) print(counter()) local i = 1 repeat name, val = debug.getupvalue(counter, i) if name then print ("index", i, name, "=", val) if(name == "n") then debug.setupvalue (counter,2,10) end i = i + 1 end -- if until not name print(counter()) --[[ 輸出結(jié)果: 1 2 index 1 k = 1 index 2 n = 2 11 --]] 在以上實(shí)例中,計(jì)數(shù)器在每次調(diào)用時(shí)都會(huì)自增1。實(shí)例中我們使用了getupvalue函數(shù)查看局部變量的當(dāng)前狀態(tài)。我們可以設(shè)置局部變量為新值。實(shí)例中,在設(shè)置前n的值為2,使用setupvalue函數(shù)將其設(shè)置為10。現(xiàn)在我們調(diào)用函數(shù),執(zhí)行后輸出為11而不是3。 調(diào)試類型 命令行調(diào)試圖形界面調(diào)試 命令行調(diào)試器有:RemDebug、clidebugger、ctrace、xdbLua、LuaInterface - Debugger、Rldb、ModDebug。 圖形界調(diào)試器有:SciTE、Decoda、ZeroBrane Studio、akdebugger、luaedit。 Lua垃圾回收 Lua采用了自動(dòng)內(nèi)存管理。 這意味著你不用操心新創(chuàng)建的對(duì)象需要的內(nèi)存如何分配出來, 也不用考慮在對(duì)象不再被使用后怎樣釋放它們所占用的內(nèi)存。 Lua運(yùn)行了一個(gè)垃圾收集器來收集所有死對(duì)象 (即在Lua中不可能再訪問到的對(duì)象)來完成自動(dòng)內(nèi)存管理的工作。 Lua中所有用到的內(nèi)存,如:字符串、表、用戶數(shù)據(jù)、函數(shù)、線程、 內(nèi)部結(jié)構(gòu)等,都服從自動(dòng)管理。 Lua實(shí)現(xiàn)了一個(gè)增量標(biāo)記-掃描收集器。它使用這兩個(gè)數(shù)字來控制垃圾收集循環(huán):垃圾收集器間歇率和垃圾收集器步進(jìn)倍率。這兩個(gè)數(shù)字都使用百分?jǐn)?shù)為單位 (例如:值100在內(nèi)部表示1)。 垃圾收集器間歇率控制著收集器需要在開啟新的循環(huán)前要等待多久。增大這個(gè)值會(huì)減少收集器的積極性。 當(dāng)這個(gè)值比100小的時(shí)候,收集器在開啟新的循環(huán)前不會(huì)有等待。設(shè)置這個(gè)值為200就會(huì)讓收集器等到總內(nèi)存使用量達(dá)到之前的兩倍時(shí)才開始新的循環(huán)。 垃圾收集器步進(jìn)倍率控制著收集器運(yùn)作速度相對(duì)于內(nèi)存分配速度的倍率。增大這個(gè)值不僅會(huì)讓收集器更加積極,還會(huì)增加每個(gè)增量步驟的長度。不要把這個(gè)值設(shè)得小于100 ,那樣的話收集器就工作的太慢了以至于永遠(yuǎn)都干不完一個(gè)循環(huán)。默認(rèn)值是200,這表示收集器以內(nèi)存分配的"兩倍"速工作。 如果你把步進(jìn)倍率設(shè)為一個(gè)非常大的數(shù)字 (比你的程序可能用到的字節(jié)數(shù)還大 10% ),收集器的行為就像一個(gè)stop-the-world收集器。接著你若把間歇率設(shè)為200 ,收集器的行為就和過去的Lua版本一樣了: 每次Lua使用的內(nèi)存翻倍時(shí),就做一次完整的收集。 垃圾回收器函數(shù) Lua提供了以下函數(shù)collectgarbage ([opt [, arg]])用來控制自動(dòng)內(nèi)存管理: collectgarbage("collect"): 做一次完整的垃圾收集循環(huán)。通過參數(shù)opt它提供了一組不同的功能。 collectgarbage("count"): 以K字節(jié)數(shù)為單位返回Lua使用的總內(nèi)存數(shù)。這個(gè)值有小數(shù)部分,所以只需要乘上 1024 就能得到Lua使用的準(zhǔn)確字節(jié)數(shù)(除非溢出)。 collectgarbage("restart"): 重啟垃圾收集器的自動(dòng)運(yùn)行。 collectgarbage("setpause"): 將arg設(shè)為收集器的間歇率。 返回間歇率的前一個(gè)值。 collectgarbage("setstepmul"): 返回步進(jìn)倍率的前一個(gè)值。 collectgarbage("step"): 單步運(yùn)行垃圾收集器。 步長"大小"由arg控制。傳入0時(shí),收集器步進(jìn)(不可分割的)一步。傳入非0值,收集器收集相當(dāng)于Lua分配這些多(K字節(jié))內(nèi)存的工作。 如果收集器結(jié)束一個(gè)循環(huán)將返回true。 collectgarbage("stop"): 停止垃圾收集器的運(yùn)行。在調(diào)用重啟前,收集器只會(huì)因顯式的調(diào)用運(yùn)行。 mytable = {"apple", "orange", "banana"} print(collectgarbage("count")) mytable = nil print(collectgarbage("count")) print(collectgarbage("collect")) print(collectgarbage("count")) --[[ 輸出結(jié)果: 21.0380859375 21.0673828125 0 19.4814453125 --]] Lua面向?qū)ο?/p> 面向?qū)ο缶幊?Object Oriented Programming,OOP)是一種非常流行的計(jì)算機(jī)編程架構(gòu)。 以下幾種編程語言都支持面向?qū)ο缶幊蹋?/p> C++JavaObjective-CSmalltalkC#Ruby 面向?qū)ο蟮奶卣?/p> 封裝:指能夠把一個(gè)實(shí)體的信息、功能、響應(yīng)都裝入一個(gè)單獨(dú)的對(duì)象中的特性。繼承:繼承的方法允許在不改動(dòng)原程序的基礎(chǔ)上對(duì)其進(jìn)行擴(kuò)充,這樣使得原功能得以保存,而新功能也得以擴(kuò)展。這有利于減少重復(fù)編碼,提高軟件的開發(fā)效率。多態(tài):同一操作作用于不同的對(duì)象,可以有不同的解釋,產(chǎn)生不同的執(zhí)行結(jié)果。在運(yùn)行時(shí),可以通過指向基類的指針,來調(diào)用實(shí)現(xiàn)派生類中的方法。抽象:抽象(Abstraction)是簡化復(fù)雜的現(xiàn)實(shí)問題的途徑,它可以為具體問題找到最恰當(dāng)?shù)念惗x,并且可以在最恰當(dāng)?shù)睦^承級(jí)別解釋問題。 Lua中的面向?qū)ο?/p> 我們知道,對(duì)象由屬性和方法組成。Lua中最基本的結(jié)構(gòu)是table,所以需要用table來描述對(duì)象的屬性。 lua中的function可以用來表示方法。那么Lua中的類可以通過table + function模擬出來。 至于繼承,可以通過metetable模擬出來(不推薦用,只模擬最基本的對(duì)象大部分實(shí)現(xiàn)夠用了)。 Lua中的表不僅在某種意義上是一種對(duì)象。像對(duì)象一樣,表也有狀態(tài)(成員變量);也有與對(duì)象的值獨(dú)立的本性,特別是擁有兩個(gè)不同值的對(duì)象(table)代表兩個(gè)不同的對(duì)象;一個(gè)對(duì)象在不同的時(shí)候也可以有不同的值,但他始終是一個(gè)對(duì)象;與對(duì)象類似,表的生命周期與其由什么創(chuàng)建、在哪創(chuàng)建沒有關(guān)系。對(duì)象有他們的成員函數(shù),表也有: Account = {balance = 0} function Account.withdraw (v) Account.balance = Account.balance - v end 這個(gè)定義創(chuàng)建了一個(gè)新的函數(shù),并且保存在Account對(duì)象的withdraw域內(nèi),我們可以這樣調(diào)用: Account.withdraw(100) 簡單實(shí)例 以下簡單的類包含了三個(gè)屬性: area、length和breadth,printArea方法用于打印計(jì)算結(jié)果: ?Rectangle = {area = 0,length = 0,width = 0} --元類 function Rectangle:new(o,length,width) o = o or {} setmetatable(o,self) self.__index = self self.length = length or 0 self.width = width or 0 self.area = length * width return o end function Rectangle:printArea() print("矩形的面積 = "..self.area) end --創(chuàng)建對(duì)象:創(chuàng)建對(duì)象是為類的實(shí)例分配內(nèi)存的過程。每個(gè)類都有屬于自己的內(nèi)存并共享公共數(shù)據(jù)。 obj = Rectangle:new(nil,10,20) print(obj.length) --訪問成員屬性 obj:printArea() --訪問成員函數(shù) --內(nèi)存在對(duì)象初始化時(shí)分配。 --[[ 輸出結(jié)果: 10 矩形的面積 = 200 --]] 完整實(shí)例 Shape = {area = 0} --元類 function Shape:new(o,side) o = o or {} setmetatable(o,self) self.__index = self side = side or 0 self.area = side * side return o end function Shape:printArea() print("面積 = "..self.area) end obj = Shape:new(nil,10) --創(chuàng)建對(duì)象 obj:printArea() --訪問成員函數(shù) --輸出:面積 = 100 Lua繼承 繼承是指一個(gè)對(duì)象直接使用另一對(duì)象的屬性和方法。可用于擴(kuò)展基礎(chǔ)類的屬性和方法。 以下演示了一個(gè)完整的繼承實(shí)例: Shape = {area = 0} --基礎(chǔ)類 function Shape:new(o,side) --基礎(chǔ)類new方法 o = o or {} setmetatable(o,self) self.__index = self side = side or 0 self.area = side * side return o end function Shape:printArea() --基礎(chǔ)類printArea方法 print("面積 = "..self.area) end shape = Shape:new(nil,10) --實(shí)例化基礎(chǔ)類Shape對(duì)象 shape:printArea() --訪問基礎(chǔ)類Shape的printArea方法 Square = Shape:new() --Square對(duì)象繼承了Shape類 function Square:new(o,side) --派生類new方法 o = o or Shape:new(o,side) setmetatable(o,self) self.__index = self return o end function Square:printArea() --派生類printArea方法 print("正方形面積 = "..self.area) end square = Square:new(nil,20) --實(shí)例化派生類Square對(duì)象 square:printArea() --訪問派生類Square的printArea方法 Rectangle = Shape:new() --Rectangle對(duì)象繼承了Shape類 function Rectangle:new(o,length,width) --派生類new方法 o = o or Shape:new(o) setmetatable(o,self) self.__index = self self.area = length * width return o end function Rectangle:printArea() --派生類printArea方法 print("矩形面積 = "..self.area) end rectangle = Rectangle:new(nil,30,30) --實(shí)例化派生類Rectangle對(duì)象 rectangle:printArea() --訪問派生類Rectangle的printArea方法 --[[ 輸出結(jié)果: 面積 = 100 正方形面積 = 400 矩形面積 = 900 --]] 函數(shù)重寫 function Square:printArea() --重寫 print("正方形面積 = "..self.area) end function Rectangle:printArea() --重寫 print("矩形面積 = "..self.area) end Lua訪問數(shù)據(jù)庫 柚子快報(bào)激活碼778899分享:Lua腳本語言(持續(xù)更新) 好文鏈接
本文內(nèi)容根據(jù)網(wǎng)絡(luò)資料整理,出于傳遞更多信息之目的,不代表金鑰匙跨境贊同其觀點(diǎn)和立場。
轉(zhuǎn)載請(qǐng)注明,如有侵權(quán),聯(lián)系刪除。