上海網(wǎng)站設(shè)計(jì)專業(yè)團(tuán)隊(duì)知乎推廣合作
文章目錄
- 官方唯一指定數(shù)據(jù)結(jié)構(gòu)--table
- table的一萬種用法
- 字典和數(shù)組
- 迭代器
- ipairs()
- pairs()
- 回到Table
在【Lua學(xué)習(xí)筆記】Lua入門中我們講到了Lua的一些入門知識(shí)點(diǎn),本文將補(bǔ)充Lua的一些進(jìn)階知識(shí)
官方唯一指定數(shù)據(jù)結(jié)構(gòu)–table
在上篇文章的最后,我們指出通過查詢_G的字符索引,發(fā)現(xiàn)table.insert實(shí)際上是一個(gè)名為table的table結(jié)構(gòu)里的索引指向的函數(shù)
實(shí)際上不僅它,所有的函數(shù),模塊,全局變量,元表
我不知道作者是出于什么樣的心理活動(dòng)寫出的Lua,但確實(shí)讓我這個(gè)初學(xué)者大為震撼。
(以下內(nèi)容摘抄自Lua語言:基礎(chǔ)知識(shí))
但是作為Lua中唯一的數(shù)據(jù)結(jié)構(gòu),table還是很萬能的:
- 它可以用任何類型作索引,不止number和string,也可以使用其他類型(甚至function和table)
- Table功能強(qiáng)大,它即可以用作字典,也可以用作數(shù)組,配合元表機(jī)制還可以模擬面向?qū)ο蟆?/li>
- Lua的很多基礎(chǔ)設(shè)施,比如模塊,全局變量,元表,都是基于table實(shí)現(xiàn)的。
table的一萬種用法
字典和數(shù)組
-- 當(dāng)成字典使用
local t = {a = 1,b = true,c = "abc",
}
-- 當(dāng)成數(shù)組使用
local t2 = {1, "aa", false}
這兩種都是很自然的用法,既能作為字典,又能作為數(shù)組
但是它也可以同時(shí)表示字典和數(shù)組
local t3 = {1, 2, 3,a = "aaa",b = "bbb",
}
print(t3[1])
print(t3.a)
結(jié)果:
1
aaa
需要注意的是其中的數(shù)組和字典是以兩種不同的方式存儲(chǔ)的local t3 = {a = "aaa",1, 2,b = "bbb",3
}
print(t3.a)
print(t3[1])
print(t3[3])
結(jié)果:
aaa
1
3
從上述例子我們能看到,數(shù)字索引直接訪問了數(shù)組元素略過了鍵值對,
使用鍵值對的key名才能訪問字典中對應(yīng)的值
使用下列模式使得它們在格式上更通用
local t3 = {[1] = 1, [2] = 2, [3] = 3,["a"] = "aaa",["b"] = "bbb",
}
但是上述只是個(gè)例子,在實(shí)踐中,我們最好不混用字典和數(shù)組,這常常會(huì)引發(fā)混亂的問題。而從設(shè)計(jì)的角度看,它違反了單一職責(zé)原則,比如空Table就存在著二義性,如果它是空的,那么請問這種情況下它是數(shù)組還是字典?這往往會(huì)導(dǎo)致使用時(shí)的各種問題,例子請看下文迭代器。
迭代器
雖然迭代器并不屬于Table的知識(shí),但我認(rèn)為在此處插入講一下是比較合適的。主要就是pairs和ipairs的區(qū)別
ipairs()
返回三個(gè)值(迭代函數(shù)、表 t
以及 0
), 如此,以下代碼
for i,v in ipairs(t) do body end
將迭代鍵值對 (1,t[1]) ,(2,t[2]), ...
,直到第一個(gè)空值。
例子:
local tab = {23,35,[3] = 45,78,[8] = 101,nil,80
}for k,v in ipairs(tab) doprint(k..":"..v)
end
輸出:
1:23
2:35
3:78
在上述例子中,ipairs遍歷了數(shù)組,但在nil時(shí)停下,實(shí)際上這個(gè)table的結(jié)構(gòu)應(yīng)該是這樣:
local tab: {[1]: integer = 23,[2]: integer = 35,[3]: integer = 45|78,[4]: nil,[5]: integer = 80,[8]: integer = 101,
}
來個(gè)更混亂的例子
local tab = {23,35,[3] = 45,78,["a"] = 5,[8] = 101,[3] = nil,1212,nil,80,["b"]=nil
}for k, v in ipairs(tab) doprint(k .. ":" .. v)
end
輸出:
1:23
2:35
3:78
4:1212這是它的實(shí)際結(jié)構(gòu)
local tab: {["a"]: integer = 5,["b"]: nil,[1]: integer = 23,[2]: integer = 35,[3]: integer|nil = 45|78,[4]: integer = 1212,[5]: nil,[6]: integer = 80,[8]: integer = 101,
}
可以看出ipair只會(huì)遍歷數(shù)字key名的元素(也就是數(shù)組類型),并且當(dāng)碰到nil時(shí)停下,而其他字典類型會(huì)被無視
而ipair會(huì)有三個(gè)返回值,分別是迭代函數(shù),表,index。讓我們看看這三個(gè)值在迭代器中是如何迭代的:
print("---index=0---")
funcA ,table, index =ipairs(tab)
for k, v in funcA, table, index doprint(k .. ":" .. v)
end
print("---index=1---")
for k, v in funcA, table, index+1 doprint(k .. ":" .. v)
end輸出:
---index=0---
1:23
2:35
3:78
4:1212
---index=1---
2:35
3:78
4:1212
從上述例子中可以看到,index實(shí)際上代表了起始序列,當(dāng)index=0,對應(yīng)從table的數(shù)組標(biāo)簽[1]開始,當(dāng)index=1,則從[2]開始
如果數(shù)組里有負(fù)數(shù)和0呢?
local tab = {[0] = 1,2,[-1] = 3,4,5,[5] = 6,
}
for k, v in ipairs(tab) doprint(k .. ":" .. v)
end
輸出:
1:2
2:4
3:5
實(shí)際的table結(jié)構(gòu)
local tab: {[0]: integer = 1,[1]: integer = 2,[-1]: integer = 3,[2]: integer = 4,[3]: integer = 5,[5]: integer = 6,
}
可以看到,0和負(fù)數(shù)都被ipairs自動(dòng)略過了,有意思的是由于[4]沒有定義,因此被認(rèn)為是nil而停止了迭代。
總結(jié):ipairs會(huì)略過數(shù)組的0和負(fù)數(shù)索引,以及其他字典索引,從數(shù)組的[1]索引開始迭代(對應(yīng)index=0),順序迭代直到某個(gè)索引不存在或其對應(yīng)的值為空時(shí)結(jié)束
pairs()
讓我們把上述幾個(gè)例子用pairs遍歷一下
local tab = {23,35,[3] = 45,78,[8] = 101,nil,80
}for k,v in pairs(tab) doprint(k..":"..v)
end
輸出:
1:23
2:35
3:78
5:80
8:101
table的結(jié)構(gòu)是這樣:
local tab: {[1]: integer = 23,[2]: integer = 35,[3]: integer = 45|78,[4]: nil,[5]: integer = 80,[8]: integer = 101,
}
可以看到重復(fù)定義的元素值還是選擇了后者,并且nil被無視了
local tab = {["b"]=8,[0] = 1,2,[-1] = 3,4,5,[5] = 6,["a"]=7,
}
for k, v in pairs(tab) doprint(k .. ":" .. v)
end
輸出:
1:2
2:4
3:5
0:1
b:8
a:7
-1:3
5:6
實(shí)際的table結(jié)構(gòu)
local tab: {[0]: integer = 1,[1]: integer = 2,[-1]: integer = 3,[2]: integer = 4,[3]: integer = 5,[5]: integer = 6,["a"]: integer = 7,["b"]: integer = 8,
}
有意思的是pairs是先按數(shù)字順序輸出了數(shù)組,然后碰到了不存在的索引[4],隨后輸出了0,b,a,-1。順序十分詭異,最后才輸出了[5]。我不知道為什么這樣輸出,但是這種輸出方式也側(cè)面證明了數(shù)組不要和字典一起定義!
總結(jié):
ipairs會(huì)略過數(shù)組非正數(shù)索引,以及其他字典索引,從數(shù)組的[1]索引開始迭代(對應(yīng)index=0),順序迭代直到某個(gè)索引不存在或其對應(yīng)的值為空時(shí)結(jié)束。
pairs可以輸出table內(nèi)除了nil以外的所有元素。但是數(shù)組和字典的混合以及帶有非正值數(shù)字索引的元素輸出方式會(huì)很詭異。
所以別用非正數(shù)索引(實(shí)際上非正索引應(yīng)當(dāng)稱為自定義索引),也別把數(shù)組和字典定義在一個(gè)table里!
回到Table
table將key的值設(shè)為nil,它的真實(shí)含義是刪除掉這個(gè)key,這和其他腳本很不一樣,也可能引發(fā)一些問題,比如看下面例子:
local t = {1, 2, nil, 4}
print(#t) ---> 4
for k, v in ipairs(t) do print(v) end ---> 1 2
for k, v in pairs(t) do print(v) end ---> 1 2 4
for i = 1, #t do print(t[i]) end ---> 1 2 nil 4
可以看到使用迭代器是直接無視nil的,但使用for遍歷會(huì)得到nil,我們本意是想刪除這個(gè)元素,但是它依然存在于table中
那么nil不等于刪除嗎?請看下列的例子:
local t = {1, 2, nil, nil, 5}
print(#t) --> 5
t = {[1]=1, [2]=2, [3]=nil, [4]=nil, [5]=4}
print(#t) --> 2
直接定義nil時(shí),nil是會(huì)計(jì)入table的長度的。但主動(dòng)定義鍵值對時(shí)nil不會(huì)計(jì)入table的長度。因此當(dāng)我們定義table時(shí),應(yīng)當(dāng)以鍵值對的方式定義
過分嗎?還有更過分的
a = { [1] = 1, [2] = 2, [5] = 5, [6] = 6 }
print(#a) -->2
b = { [1] = 1, [2] = 2, [4] = 4, [5] = 5, [6] = 6 }
print(#b) -->6
c = { [1] = 1, [2] = 2, [4] = 4, [5] = 5, [6] = 6, [9] = 9 }
print(#c) -->6
d = { [1] = 1, [2] = 2, [4] = 4, [5] = 5, [6] = 6, [8] = 8, [9] = 9 }
print(#d) -->6
發(fā)現(xiàn)了嗎?鍵值對形式存儲(chǔ)時(shí),中間如果隔了一個(gè)nil,那么長度會(huì)接上;如果隔了兩個(gè)nil長度就會(huì)斷開
???
🧠
(O.o)>