1.1 實例代碼
function createCountdownTimer(second)
local ms = second * 1000 --ms為countDown的Upvalue
local function countDown()
ms = ms -1
return ms
end
return countDown
end
local timer1 = createCountdownTimer(1)
for i = 1, 3 do
print(timer1())
end
輸出結果:
999
998
997
1.2 關于函數(shù)閉包描述
Upvalue
一個函數(shù)所使用的定義在它的函數(shù)體之外的局部變量(external local variable)稱為這個函數(shù)的upvalue。 在前面的代碼中,函數(shù)countDown使用的定義在函數(shù)createCountdownTimer 中的局部變量ms就是countDown的upvalue,但ms對createCountdownTimer而 言只是一個局部變量,不是upvalue。 Upvalue是Lua不同于C/C++的特有屬性,需要結合代碼仔細體會。
一個函數(shù)和它所使用的所有upvalue構成了一個函數(shù)閉包。
Lua函數(shù)閉包使函數(shù)具有保持它自己的狀態(tài)的能力,從這個意義上說,可以 與帶靜態(tài)局部變量的C函數(shù)相類比。但二者有顯著的不同:對Lua來說,函數(shù) 是一種基本數(shù)據(jù)類型——代表一種(可執(zhí)行)對象,可以有自己的狀態(tài);但 是對帶靜態(tài)局部變量的C函數(shù)來說,它并不是C的一種數(shù)據(jù)類型,更不會產(chǎn)生 什么對象實例,它只是一個靜態(tài)地址的符號名稱。
2.2 實例代碼
local function create(name ,id )
local data = {name = name ,id = id} --data為obj.SetName,obj.GetName,obj.SetId,obj.GetId的Upvalue
local obj = {} --把需要隱藏的成員放在一張表里,把該表作為成員函數(shù)的upvalue。
function obj.SetName(name)
data.name = name
end
function obj.GetName()
return data.name
end
function obj.SetId(id)
data.id = id
end
function obj.GetId()
return data.id
end
return obj
end
輸出結果:
mycreate's id:1mycreate's name:Sam
mycreate's id:1mycreate's name:Lucy
2.2 有關對象實現(xiàn)的描述
實現(xiàn)方式: 把需要隱藏的成員放在一張表里,把該表作為成員函數(shù)的upvalue。
局限性: 基于對象的實現(xiàn)不涉及繼承及多態(tài)。但另一方面,腳本編程是否需要繼承和多態(tài)要視情況而定。
3.1 實例代碼(1):
local t = {}
local m = {a = "and",b = "Li Lei", c = "Han Meimei"}
setmetatable(t,{__index = m}) --表{ __index=m }作為表t的元表
for k,v in pairs(t) do --窮舉表t
print("有值嗎?")
print(k,"=>",v)
end
print("-------------")
print(t.b, t.a, t.c)
輸出結果:
Li LeiandHan Meimei
3.2 實例代碼(2):
local function add(t1,t2)
--‘#’運算符取表長度
assert(#t1 == #t2)
local length = #t1
for i = 1,length do
t1[i] = t1[i] + t2[i]
end
return t1
end
--setmetatable返回被設置的表
t1 = setmetatable({1,2,3},{__add = add})
t2 = setmetatable({10,20,30},{__add = add})
for k,v in pairs(t1) do
print(k,"=>",v)
end
for k,v in pairs(t2) do
print(k,"=>",v)
end
print("---------兩元表相加--------------")
t1 = t1 + t2
for i = 1 ,#t1 do
print(t1[i])
end
輸出結果:
1=>1
2=>2
3=>3
1=>10
2=>20
3=>30
---------兩元表相加--------------
11
22
33
3.3 有關元表的描述:
定義 :
元表本身只是一個普通的表,通過特定的方法(比如setmetatable)設置到某個對象上,進而影響這個對象的行為;一個對象有哪些行為受到元表影響以及這些行為按照何種方式受到影響是受Lua語言約束的。比如在前面的代碼里,兩個表對象的加法運算,如果沒有元表的干預,就是一種錯誤;但是Lua規(guī)定了元表可以“重載”對象的加法運算符,因此若把定義了加法運算的元表設置到那兩個表上,它們就可以做加法了。元表是Lua最關鍵的概念之一,內容也很豐富,請參考Lua文檔了解詳情。
元表與C++虛表的比較:
如果把表比作對象,元表就是可以改變對象行為的“元”對象。在某種程度上,元表可以與C++的虛表做一類比。但二者還是迥然不同的:元表可以動態(tài)的改變,C++虛表是靜態(tài)不變的;元表可以影響表(以及其他類型的對象)的很多方面的行為,虛表主要是為了定位對象的虛方法(最多再帶上一點點RTTI)。
4.1 實例代碼
local Robot = { name = "Sam", id = 001 }
function Robot:New(extension)
local t = setmetatable(extension or { }, self)
self.__index = self
return t
end
function Robot:SetName(name)
self.name = name
end
function Robot:GetName()
return self.name
end
function Robot:SetId(id)
self.id = id end
function Robot:GetId()
return self.id
end
robot = Robot:New()
print("robot's name:", robot:GetName())
print("robot's id:", robot:GetId())
print("-----------------")
local FootballRobot = Robot:New({position = "right back"})
function FootballRobot:SetPosition(p)
self.position = p
end
function FootballRobot:GetPosition()
return self.position
end
fr = FootballRobot:New()
print("fr's position:", fr:GetPosition())
print("fr's name:", fr:GetName())
print("fr's id:", fr:GetId())
print("-----------------")
fr:SetName("Bob")
print("fr's name:", fr:GetName())
print("robot's name:", robot:GetName())
輸出結果:
robot's name:Sam
robot's id:1
fr's position:right back
fr's name:Sam
fr's id:1
fr's name:Bob
robot's name:Sam
4.2 相關描述:
prototype模式一個對象既是一個普通的對象,同時也可以作為創(chuàng)建其他對象的原型的對象(即類對象,class object);動態(tài)的改變原型對象的屬性就可以動態(tài)的影響所有基于此原型的對象;另外,基于一個原型被創(chuàng)建出來的對象可以重載任何屬于這個原型對象的方法、屬性而不影響原型對象;同時,基于原型被創(chuàng)建出來的對象還可以作為原型來創(chuàng)建其他對象。
5.1 實例代碼:
hello.lua
local pack = require "mypack" --導入包[注:包的名字與定義包的文件的名字相同(除去文件名后綴,在前面的代碼中,就是“mypack”)]
print(ver or "No ver defined!")
print(pack.ver)
pack.aFunInMyPack()
print(aFunInMyPack or "No aFunInMyPack defined!")
aFuncFromMyPack()
mypack.lua
module(..., package.seeall) --定義包
ver = "0.1 alpha"
function aFunInMyPack()
print("Hello!")
end
_G.aFuncFromMyPack = aFunInMyPack
輸出結果:
No ver defined!
0.1 alpha
Hello!
No aFunInMyPack defined!
Hello!
5.2有關包的描述:
包是一種組織代碼的方式。
一般在一個Lua文件內以module函數(shù)開始定義一個包。module同時定義了一個新的包的函數(shù)環(huán)境,以使在此包中定義的全局變量都在這個環(huán)境中,而非使用包的函數(shù)的環(huán)境中。理解這一點非常關鍵。以前面的代碼為例, “module(..., package.seeall)”的意思是定義一個包,包的名字與定義包的文件的名字相同(除去文件名后綴,在前面的代碼中,就是“mypack”),并且在包的函數(shù)環(huán)境里可以訪問使用包的函數(shù)環(huán)境(比如,包的實現(xiàn)使用了print,這個變量沒有在包里定義,而是定義在使用包的外部環(huán)境中)。
一般用require函數(shù)來導入一個包,要導入的包必須被置于包路徑(packagepath)上。包路徑可以通過package.path或者環(huán)境變量來設定。一般來說,當前工作路徑總是在包路徑中。
現(xiàn)在官網(wǎng)已經(jīng)不提倡使用module了,官方給出了兩個理由(以?
model(... , package ,seeall)
?為例):package.seeall 這種方式破壞了模塊的高內聚,原本引入 "filename" 模塊只想調用它的 foobar() 函數(shù),但是它卻可以讀寫全局屬性,例如 "filename.os"。
module 函數(shù)壓棧操作引發(fā)的副作用,污染了全局環(huán)境變量。例如 module("filename") 會創(chuàng)建一個 filename 的 table,并將這個 table 注入全局環(huán)境變量中,這樣使得沒有引用它的文件也能調用 filename 模塊的方法。
請參考Lua手冊進一步了解包的詳細說明。
參考文獻《C/C++程序員的Lua快速入門》
更多建議: