最近要用到 Lua 編程語(yǔ)言,所以學(xué)習(xí)了一些簡(jiǎn)明教程敏簿,同時(shí)記錄一下 Lua 編程語(yǔ)言相對(duì)于其他主流編程語(yǔ)言在語(yǔ)法上特殊的地方锣吼。其中女嘲,在 Lua 中使用Table
數(shù)據(jù)結(jié)構(gòu)實(shí)現(xiàn)“面向?qū)ο蟆本幊淌侵攸c(diǎn)募狂。
注釋
-- 單行注釋
--[[
塊注釋办素,有趣的是這個(gè)注釋標(biāo)記不是對(duì)稱的
--]]
變量
- 變量沒(méi)有類型,值才有類型祸穷,也就是在聲明變量的時(shí)候不需要聲明變量的類型性穿。
- 數(shù)字只有
double
類型 。 - 沒(méi)有定義過(guò)的變量值為
nil
雷滚。 - 對(duì)于布爾類型需曾,只有
nil
和false
表示假,其他的值都為真。 - 默認(rèn)變量都是全局變量胯舷,局部變量需要加
local
關(guān)鍵字
操作符
- Lua 中沒(méi)有
++
和+=
這類的運(yùn)算符 - 不等于號(hào)是
~=
- 字符串鏈接符是
..
- 條件表達(dá)的“與”刻蚯、“或”、“非”分別是
and
,or
,not
控制語(yǔ)句
if-else 分支
i = 10
if i = 0 then
-- do something
elseif i > 5 and i < 10 then
-- do something
else
-- do something
end
while 循環(huán)
i = 0
while i < 100 do
-- do something
i = i + 1
end
until 循環(huán)
sum = 2
repeat
sum = sum ^ 2
until sum > 100
for 循環(huán)
for i=1, 100, 2 do
-- do something
end
函數(shù)
函數(shù)可以作為返回值返回
function add(x)
return function(y) y + x end
end
addOne = add(1)
addOne(1) -- 2
可以返回多個(gè)值桑嘶,同時(shí)默認(rèn)為全局函數(shù)。
值得注意的是 Lua 的函數(shù)參數(shù)不支持默認(rèn)值躬充,這也為 Lua 程序的重構(gòu)帶來(lái)了一些麻煩逃顶。
Table
Table
是 Lua 中重要的數(shù)據(jù)結(jié)構(gòu),它是由一系列的 key-value 鍵值對(duì)組成充甚。Lua 中的數(shù)組也是一種特殊的 Table
以政,它下標(biāo)從 1 開(kāi)始,而且在一個(gè)數(shù)組中可以有不同類型的成員伴找。
變量對(duì)于 Table
的引用是弱引用盈蛮,也就是說(shuō) Table
是獨(dú)立于變量存在的,只有當(dāng)沒(méi)有任何一個(gè)變量引用這個(gè) Table
的時(shí)候技矮,Lua 的垃圾回收機(jī)制才會(huì)把這個(gè) Table
從內(nèi)存中回收抖誉。這個(gè)特性對(duì)于在 Lua 中實(shí)現(xiàn)“面向?qū)ο蟆币埠苤匾?/p>
-- 定義和訪問(wèn)
person = {name = "jack", age =24}
person.name = "black"
person.age = 20
-- 另一種定義和訪問(wèn)
person2 = {['name']="green", ['age']=30}
person2['name'] = "tom"
person2['age'] = 10
-- 數(shù)組
arr = {1,2,3,4,5}
arr = {[1]=1,[2]=2,[3]=3,[4]=4,[5]=5} -- 與上面的定義等價(jià)
MetaTable 和 MetaMethod
Lua 中每個(gè)值都有一套預(yù)定義的操作集合,這個(gè)集合就是 MateTable 衰倦,MetaTable 中預(yù)定義的方法就是 MetaMethod袒炉。table
和 userdata
有各自獨(dú)立的 MetaTable,從而可以利用這一特性實(shí)現(xiàn)“面向?qū)ο蟆本幊谭恪6渌愋偷闹祫t共享屬于該類型的一個(gè) MetaTable我磁。
面向?qū)ο?/h1>
Lua 可以通過(guò)使用 Table
這種數(shù)據(jù)結(jié)構(gòu)來(lái)實(shí)現(xiàn)面向?qū)ο缶幊獭5沁@種面向?qū)ο蟛⒉皇腔陬悾?code>Class)的驻襟,而是基于原型(prototype
)的夺艰。這個(gè)原型就是我們定義好的一個(gè) Table
,其他對(duì)象就可以通過(guò)這個(gè)原型衍生出來(lái)沉衣。
定義原型
首先定義一個(gè) Table
當(dāng)作我們的原型郁副,并定義一個(gè)成員變量
Account = { balance = 0 }
這樣原型就定義好了,由于 Table
是獨(dú)立于變量存在的厢蒜, 只要不把 Account
變量設(shè)為 nil
霞势,那么就這個(gè)原型就一直存在。在這個(gè)原型中有一個(gè) balance
的成員變量斑鸦。
定義成員方法
緊接上面的代碼愕贡,我們可以定義一個(gè)成員方法
function Account.withdraw(self, v)
self.balance = self.balance - v
end
其中 Account.withdraw
是一個(gè)語(yǔ)法糖, 相當(dāng)于在 Account
的 Table 中定義了一個(gè) withdraw
的字段巷屿,而它的內(nèi)容就是一個(gè)方法固以,上面的寫法等價(jià)于:
Account = {
withdraw = function (self, v)
self.balance = self.balance - v
end
}
在這個(gè)方法中使用了 self
關(guān)鍵字,它就是指這個(gè) Table
本身,它有可能是這個(gè)原型憨琳,也可能是由這個(gè)原型衍生出的對(duì)象诫钓。
同時(shí)我們可以在定義成員方法時(shí)使用 :
語(yǔ)法糖,默認(rèn)傳入 self
提高編碼效率篙螟。例如:
function Account:withdraw(v)
self.balance = self.balance - v
end
生成對(duì)象
對(duì)象其實(shí)也是一個(gè) Table
, 只不過(guò)這個(gè) Table
并不是空的菌湃,而是基于一個(gè)原型產(chǎn)生的”槁裕基于原型的 Table
就是利用上面的 MetaTable 來(lái)實(shí)現(xiàn)的惧所。同樣基于上面的代碼實(shí)現(xiàn)一個(gè)產(chǎn)生基于 Account
原型的對(duì)象的方法new
:
function Account:new(o)
if o == nil then
o = {}
end
setmetatable(o, self) -- 綁定原型
self.__index = self --索引原型,方便使用點(diǎn)號(hào)
return o
end
這個(gè)方法的關(guān)鍵就是在對(duì)象的 MetaTable 中添加了原型 Account
绪杏。這樣當(dāng)我們要訪問(wèn)對(duì)象 o
這個(gè)中的某個(gè)未定義的字段時(shí)下愈,Lua 就會(huì)去查找它的 MetaTable 中是否有這個(gè)字段,由于我們綁定了原型蕾久,這樣就能找到原型中的這個(gè)字段势似。比如,我們并沒(méi)有為 o
定義 withdraw
方法僧著,所以當(dāng)我們?cè)L問(wèn) o
的 withdraw 字段時(shí)履因,就會(huì)發(fā)現(xiàn) o
本身的 Table
并沒(méi)有這個(gè)字段,然后去查找它的 MetaTable霹抛,這樣就能訪問(wèn)到原型搓逾,也就是 Account
的 withdraw
的實(shí)現(xiàn)。
派生
由原有的原型派生出新的原型在 Lua 中實(shí)現(xiàn)起來(lái)也不難杯拐,因?yàn)樵鸵彩?Table
霞篡, 我們只要為這個(gè) Table
添加新的字段,或者為原有字段定義新的內(nèi)容就行了端逼,例如:
SpecialAccount = Account:new{limit = 100}
-- 重新定義 withdraw 方法
function SpecialAccount:withdraw(v)
if v < self.limit then
self.balance = self.balance - v
end
end
在上面的代碼中就從 Account
原型中派生出了一個(gè)新的原型朗兵,其中添加了一個(gè)新的limit
成員變量和重新定義了withdraw
成員方法。