資料表教學

lua-users home
wiki

在 Lua 中,資料表是唯一的「容器」類型。它們是關聯陣列 ([1]),意即它們儲存一組金鑰/值對。在金鑰/值對中,你可以將一個值儲存在一個金鑰之下,然後之後再使用這個金鑰檢索該值。

建立資料表

使用大括弧定義的表格建構子建立資料表,例如: { } 。要定義一個空資料表,我們可以執行以下操作。

> t = {}    -- construct an empty table and assign it to variable "t"
> print(t)
table: 0035AE18
請注意,當顯示資料表的時,只會顯示物件的型別和唯一識別碼。要列印資料表的內容,我們必須明確這麼做。我們稍後會說明如何進行此操作。

使用資料表

要存取資料表中與金鑰關聯的值,可以使用 table[key] 語法

> t = {}
> t["foo"] = 123 -- assign the value 123 to the key "foo" in the table
> t[3] = "bar" -- assign the value "bar" to the key 3 in the table
> = t["foo"]
123
> = t[3]
bar

如果沒有與金鑰關聯的值,這並非錯誤,結果將會是 nil

> t = {}
> = t["foo"]
nil

你可以透過將 nil 指定給值來從資料表中刪除一個金鑰/值對。

> t["foo"] = nil

任何值都可以用作金鑰(不只是數字和字串),只要確保它不是 nil 或 NaN(非數字)即可

> t = {}
> k = {}
> f = function () end
> t[k] = 123
> t[f] = 456
> = t[k]
123
> = t[f]
456
> t[nil] = 123
stdin:1: table index is nil
stack traceback:
        stdin:1: in main chunk
        [C]: in ?
> t[0/0] = 123
stdin:1: table index is NaN
stack traceback:
        stdin:1: in main chunk
        [C]: in ?

但是使用字串常數作為金鑰非常普遍,因此有一個特殊的捷徑語法

> t = {}
> t.foo = 123 -- same as t["foo"] (but not t[foo], which would use the variable foo as the key)
> = t.foo
123
> = t["foo"]
123
只有當字串包含底線、字元和數字,但不能以數字開頭時,才有效捷徑語法。

你也可以直接在 {} 語法裡面新增金鑰/值關聯。

> t = {["foo"] = "bar", [123] = 456}
> = t.foo
bar
> = t[123]
456

還有一個適用於 {} 中字串金鑰的語法捷徑,只要字串符合與 . 語法相同的規則即可

> t = {foo = "bar"} -- same as ["foo"]="bar" (but not [foo]="bar" , that would use the variable foo)
> = t["foo"]
bar

要迴圈遍歷資料表中的所有金鑰/值對,請使用 pairs 迭代器

> t = {foo = "bar", [123] = 456}
> for key,value in pairs(t) do print(key,value) end
foo bar
123 456
使用 pairs 迴圈的順序是不確定的。即使你是在新增另一個項目後才新增一個項目,也不表示它們在 pairs 中的順序也會如此。

pairs 迴圈內,重新指派現有金鑰或刪除金鑰(透過將 nil 指定給它們)是安全的,但不要新增新的金鑰(先前具有 nil 值)。

資料表作為陣列

首先,記住資料表仍然只是金鑰/值容器,因為 Lua 實際上並沒有陣列類型。但資料表可以被當作陣列來處理,此處有說明

資料表建構子可以包含以逗號分隔的物件清單來建立「陣列」

> t = {"a", "b", "c"}
> = t[1]
a
> = t[3]
c
這是以下內容的語法捷徑
> t = {[1]="a", [2]="b", [3]="c"}
> = t[1]
a
> = t[3]
c
所以它仍然只是一個金鑰/值關聯。

你也可以將陣列語法與一般的 key=value 語法混合使用

> t = {"a", "b", [123]="foo", "c", name="bar", "d", "e"}
> for k,v in pairs(t) do print(k,v) end
1       a
2       b
3       c
4       d
5       e
123     foo
name    bar

第一個索引是數字一,這與大多數從數字零開始的其他語言不同。選擇這個數字的原因是因為它通常更直觀。它也被選為金鑰,而不是開始的偏移量。大多數語言實作實際的陣列類型。

你可以使用 # 運算子來取得陣列的「長度」

> t = {"a", "b", "c"}
> = #t
3

# 算符不會計算表格中所有項目(!)。而是會找出最後一個整數(非分數)鍵。由於其實作方式,如果表格中的所有整數鍵不是連續的,其結果便未定義。這就是為什麼不應將其用於當作稀疏陣列所用的表格[2]

有兩種方式可將項目新增到陣列的尾端

> t = {}
> table.insert(t, 123)
> t[#t+1] = 456
> = t[1]
123
> = t[2]
456

table.insert 採用一個選用的索引參數來插入至陣列的中央。它會向上移動索引上方的任何其他整數鍵

> t = {"a", "c"}
> table.insert(t, 2, "b")
> = t[1], t[2], t[3]
a b c

table.remove 移掉陣列中的項目,向下移動任何尚存的整數鍵

> t = {"a", "b", "c"}
> table.remove(t, 2)
> = t[1], t[2]
a c

若要迴圈陣列,請使用 ipairs。與對齊(pairs)不同,它僅提供連續的整數鍵,且起始於 1。它保證順序。使用對齊(pairs)時,數字鍵不一定要以正確順序提供!

> t = {"a", "b", "c"}
> for i, v in ipairs(t) do print(i, v) end
1       a
2       b
3       c

若要連接一組字串,則使用 table.concat。它採用選用的分隔符號、開頭及結尾參數。此處我們僅使用分隔符號

> t = {"a", "b", "c"}
> = table.concat(t, ";")
a;b;c

若要取得所有 table.* 函數清單及其完整的文件,請參閱 [[3]]

表格值是參考

當您傳遞一個表格給函數,或將其儲存在新的變數中等等動作時,並不會建立該表格的新拷貝。這種狀況時,表格並不像數字那樣作用。相反地,變數或函數會變成對原始表格的參考。這很像 C 語言中的指標。例如

> t = {}
> u = t
> u.foo = "bar"
> = t.foo
bar
> function f(x) x[1] = 2 end
> f(t)
> = u[1]
2

表格會在對其的最後一次參考消失後,由垃圾回收器從記憶體中移除。然而,這並非總會立即發生。垃圾回收器設計用於正確地運作,即使一個表格(直接或間接)包含對本身的參考。也將發生。

還有一件相關的事情要記住:表格比較是透過參考來運作的。使用 == 比較表格時,即使兩個表格有相同的內容,仍會傳回 false。它們必須實際上是對相同表格的參考。

最後,如果您要複製一張表格,必須要手動執行。Lua 沒有提供標準的函數,主要是因為有許多不同的方式可以複製一張表格。

未排序集合的表格

經常有一些初學者會建立一個陣列來存放一組物件,即使不需要順序也是如此。這樣做會出現一個問題,那就是移除的動作比較慢,因為電腦必須向下移動其他項目。檢查陣列中是否有一個項目,這個動作也很慢,因為電腦必須迴圈處理所有項目。

透過將項目儲存在鍵中,並將值設定為虛擬值(例如 true),即可解決這個問題。這樣做將能幫助您將表格用成一個未排序的集合,可以快速插入、移除及查詢。

主要的差異點是,沒有簡單的方法來取得計數(您必須使用迴圈),而且您無法在集合中儲存同一項兩次。

所以如果您需要儲存一組項目,最好同時考慮集合及陣列,選擇最適合您現在情況的

local items = {}

-- add some items to the set
items["foo"] = true
items[123] = true

-- is "foo" in the set?
if items["foo"] then
  -- do stuff
end

-- remove item from the set
items[123] = nil

最近變更 · 偏好設定
編輯 · 歷史
最後編輯 2020 年 9 月 23 日 下午 8:38 GMT (差異)