深入談?wù)刲ua中神奇的table
最近在嘗試配置 awesome WM,因此粗略地學(xué)習(xí)了一下 lua 。 在學(xué)習(xí)過程中,我完全被 table 在 lua 中的應(yīng)用所鎮(zhèn)住了。
table 在 lua 中真的是無處不在:首先,它可以作為字典和數(shù)組來用; 此外,它還可以被用于設(shè)置閉包環(huán)境、module; 甚至可以用來模擬對(duì)象和類
字典
table 最基礎(chǔ)的作用就是當(dāng)成字典來用。 它的 key 值可以是除了 nil 之外的任何類型的值。
t={} t[{}] = "table" -- key 可以是 table t[1] = "int" -- key 可以是整數(shù) t[1.1] = "double" -- key 可以是小數(shù) t[function () end] = "function" -- key 可以是函數(shù) t[true] = "Boolean" -- key 可以是布爾值 t["abc"] = "String" -- key 可以是字符串 t[io.stdout] = "userdata" -- key 可以是userdata t[coroutine.create(function () end)] = "Thread" -- key可以是thread
當(dāng)把 table 當(dāng)成字典來用時(shí),可以使用 pairs 函數(shù)來進(jìn)行遍歷。
for k,v in pairs(t) do print(k,"->",v) end
運(yùn)行結(jié)果為:
1 -> int
1.1 -> double
thread: 0x220bb08 -> Thread
table: 0x220b670 -> table
abc -> String
file (0x7f34a81ef5c0) -> userdata
function: 0x220b340 -> function
true -> Boolean
從結(jié)果中你還可以發(fā)現(xiàn),使用 pairs 進(jìn)行遍歷時(shí)的順序是隨機(jī)的,事實(shí)上相同的語句執(zhí)行多次得到的結(jié)果是不一樣的。
table 中的key最常見的兩種類型就是整數(shù)型和字符串類型。 當(dāng) key 為字符串時(shí),table可以當(dāng)成結(jié)構(gòu)體來用。同時(shí)形如 t["field"] 這種形式的寫法可以簡寫成 t.field 這種形式。
數(shù)組
當(dāng) key 為整數(shù)時(shí),table 就可以當(dāng)成數(shù)組來用。而且這個(gè)數(shù)組是一個(gè) 索引從1開始 ,沒有固定長度,可以根據(jù)需要自動(dòng)增長的數(shù)組。
a = {} for i=0,5 do -- 注意,這里故意寫成了i從0開始 a[i] = 0 end
當(dāng)將 table 當(dāng)成數(shù)組來用時(shí),可以通過 長度操作符 # 來獲取數(shù)組的長度
print(#a)
結(jié)果為
5
你會(huì)發(fā)現(xiàn), lua 認(rèn)為 數(shù)組 a 中只有5個(gè)元素,到底是哪5個(gè)元素呢?我們可以使用使用 ipairs 對(duì)數(shù)組進(jìn)行遍歷:
for i,v in ipairs(a) do print(i,v) end
結(jié)果為
1 0
2 0
3 0
4 0
5 0
從結(jié)果中你會(huì)發(fā)現(xiàn) a 的0號(hào)索引并不認(rèn)為是數(shù)組中的一個(gè)元素,從而也驗(yàn)證了 lua 中的數(shù)組是從 1開始索引的
另外,將table當(dāng)成數(shù)組來用時(shí),一定要注意索引不連貫的情況,這種情況下 # 計(jì)算長度時(shí)會(huì)變得很詭異
a = {} for i=1,5 do a[i] = 0 end a[8] = 0-- 雖然索引不連貫,但長度是以最大索引為準(zhǔn) print(#a) a[100] = 0-- 索引不連貫,而且長度不再以最大索引為準(zhǔn)了 print(#a)
結(jié)果為:
8
8
而使用 ipairs 對(duì)數(shù)組進(jìn)行遍歷時(shí),只會(huì)從1遍歷到索引中斷處
for i,v in ipairs(a) do print(i,v) end
結(jié)果為:
1 0
2 0
3 0
4 0
5 0
環(huán)境(命名空間)
lua將所有的全局變量/局部變量保存在一個(gè)常規(guī)table中,這個(gè)table一般被稱為全局或者某個(gè)函數(shù)(閉包)的環(huán)境。
為了方便,lua在創(chuàng)建最初的全局環(huán)境時(shí),使用全局變量 _G 來引用這個(gè)全局環(huán)境。因此,在未手工設(shè)置環(huán)境的情況下,可以使用 _G[varname] 來存取全局變量的值.
for k,v in pairs(_G) do print(k,"->",v) end
rawequal -> function: 0x41c2a0
require -> function: 0x1ea4e70
_VERSION -> Lua 5.3
debug -> table: 0x1ea8ad0
string -> table: 0x1ea74b0
xpcall -> function: 0x41c720
select -> function: 0x41bea0
package -> table: 0x1ea4820
assert -> function: 0x41cc50
pcall -> function: 0x41cd10
next -> function: 0x41c450
tostring -> function: 0x41be70
_G -> table: 0x1ea2b80
coroutine -> table: 0x1ea4ee0
unpack -> function: 0x424fa0
loadstring -> function: 0x41ca00
setmetatable -> function: 0x41c7e0
rawlen -> function: 0x41c250
bit32 -> table: 0x1ea8fc0
utf8 -> table: 0x1ea8650
math -> table: 0x1ea7770
collectgarbage -> function: 0x41c650
rawset -> function: 0x41c1b0
os -> table: 0x1ea6840
pairs -> function: 0x41c950
arg -> table: 0x1ea9450
table -> table: 0x1ea5130
tonumber -> function: 0x41bf40
io -> table: 0x1ea5430
loadfile -> function: 0x41cb10
error -> function: 0x41c5c0
load -> function: 0x41ca00
print -> function: 0x41c2e0
dofile -> function: 0x41cbd0
rawget -> function: 0x41c200
type -> function: 0x41be10
getmetatable -> function: 0x41cb80
module -> function: 0x1ea4e00
ipairs -> function: 0x41c970
從lua 5.2開始,可以通過修改 _ENV 這個(gè)值(lua5.1中的setfenv從5.2開始被廢除)來設(shè)置某個(gè)函數(shù)的環(huán)境,從而讓這個(gè)函數(shù)中的執(zhí)行語句在一個(gè)新的環(huán)境中查找全局變量的值。
a=1 -- 全局變量中a=1 local env={a=10,print=_G.print} -- 新環(huán)境中a=10,并且確保能訪問到全局的print函數(shù) function f1() local _ENV=env print("in f1:a=",a) a=a*10-- 修改的是新環(huán)境中的a值 end f1() print("globally:a=",a) print("env.a=",env.a)
in f1:a= 10 globally:a= 1 env.a= 100
另外,新創(chuàng)建的閉包都繼承了創(chuàng)建它的函數(shù)的環(huán)境
module
lua 中的模塊也是通過返回一個(gè)table來供模塊使用者來使用的。 這個(gè) table中包含的是模塊中所導(dǎo)出的所有東西,包括函數(shù)和常量。
定義module的一般模板為
module(模塊名, package.seeall)
其中 module(模塊名) 的作用類似于
local modname = 模塊名
local M = {} -- M即為存放模塊所有函數(shù)及常數(shù)的table
_G[modname] = M
package.loaded[modname] = M
setmetatable(M,{__index=_G}) -- package.seeall可以使全局環(huán)境_G對(duì)當(dāng)前環(huán)境可見
local _ENV = M -- 設(shè)置當(dāng)前的運(yùn)行環(huán)境為 M,這樣后續(xù)所有代碼都不需要限定模塊名了,所定義的所有函數(shù)自動(dòng)變成M的成員
<函數(shù)定義以及常量定義>return M -- module函數(shù)會(huì)幫你返回module table,而無需手工返回
對(duì)象
lua 中之所以可以把table當(dāng)成對(duì)象來用是因?yàn)?
函數(shù)在 lua 中是一類值,你可以直接存取table中的函數(shù)值。 這使得一個(gè)table既可以有自己的狀態(tài),也可以有自己的行為:
Account = {balance = 0} function Account.withdraw(v) Account.balance = Account.balance - v end
lua 支持閉包,這個(gè)特性可以用來模擬對(duì)象的私有成員變量
function new_account(b) local balance = b return {withdraw = function (v) balance = balance -v end, get_balance = function () return balance end } end a1 = new_account(1000) a1.withdraw(10) print(a1.get_balance())
990
不過,上面第一種定義對(duì)象的方法有一個(gè)缺陷,那就是方法與 Account 這個(gè)名稱綁定死了。 也就是說,這個(gè)對(duì)象的名稱必須為 Accout 否則就會(huì)出錯(cuò)
a = Account Account = nil a.withdraw(10) -- 會(huì)報(bào)錯(cuò),因?yàn)锳ccout.balance不再存在
為了解決這個(gè)問題,我們可以給 withdraw 方法多一個(gè)參數(shù)用于指向?qū)ο蟊旧?/p>
Account = {balance=100} function Account.withdraw(self,v) self.balance = self.balance - v end a = Account Account = nil a.withdraw(a,10) -- 沒問題,這個(gè)時(shí)候 self 指向的是a,因此會(huì)去尋找 a.balance print(a.balance)
90
不過由于第一個(gè)參數(shù) self 幾乎總是指向調(diào)用方法的對(duì)象本身,因此 lua 提供了一種語法糖形式 object:method(...) 用于隱藏 self 參數(shù)的定義及傳遞. 這里冒號(hào)的作用有兩個(gè),其在定義函數(shù)時(shí)往函數(shù)中地一個(gè)參數(shù)的位置添加一個(gè)額外的隱藏參數(shù) sef, 而在調(diào)用時(shí)傳遞一個(gè)額外的隱藏參數(shù) self 到地一個(gè)參數(shù)位置。 即 function object:method(v) end
等價(jià)于 function object.method(self,v) end, object:method(v)
等價(jià)于 object.method(object,v)
類
當(dāng)涉及到類和繼承時(shí),就要用到元表和元方法了。事實(shí)上,對(duì)于 lua 來說,對(duì)象和類并不存在一個(gè)嚴(yán)格的劃分。
當(dāng)一個(gè)對(duì)象被另一個(gè)table的 __index 元方法所引用時(shí),table就能引用該對(duì)象中所定義的方法,因此也就可以理解為對(duì)象變成了table的類。
類定義的一般模板為:
function 類名:new(o) o = o or {} setmetatable(o,{__index = self}) return o end
或者
function 類名:new(o) o = o or {} setmetatable(o,self) self.__index = self return o end
相比之下,第二種寫法可以多省略一個(gè)table
另外有一點(diǎn)我覺得有必要說明的就是 lua 中的元方法是在元表中定義的,而不是對(duì)象本身定義的,這一點(diǎn)跟其他面向?qū)ο蟮恼Z言比較不同。
總結(jié)
以上就是這篇文章的全部內(nèi)容了,希望本文的內(nèi)容對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,如果有疑問大家可以留言交流,謝謝大家對(duì)本站的支持。
版權(quán)聲明:本站文章來源標(biāo)注為YINGSOO的內(nèi)容版權(quán)均為本站所有,歡迎引用、轉(zhuǎn)載,請(qǐng)保持原文完整并注明來源及原文鏈接。禁止復(fù)制或仿造本網(wǎng)站,禁止在非www.sddonglingsh.com所屬的服務(wù)器上建立鏡像,否則將依法追究法律責(zé)任。本站部分內(nèi)容來源于網(wǎng)友推薦、互聯(lián)網(wǎng)收集整理而來,僅供學(xué)習(xí)參考,不代表本站立場,如有內(nèi)容涉嫌侵權(quán),請(qǐng)聯(lián)系alex-e#qq.com處理。