Lua 程式設計教學:表 (table)

PUBLISHED ON JAN 20, 2018 — PROGRAMMING

    表 (table) 是 Lua 唯一的資料結構,在 Lua 內部的實作上,表帶有雜湊表 (hash table) 和陣列 (array) 兩種資料結構,Lua 會視需求自動呼叫適合的結構。對使用者來說,不需要去區分這些細節,當成關聯式陣列 (associative array) 使用即可。

    表有許多的用途:

    • 做為陣列 (array)
    • 做為字典 (dictionary) 或雜湊 (hash)
    • 用於物件導向程式 (object-oriented programming)
    • 模擬其他資料結構 (data structures)

    本文著重前兩者,其他的用途於後文介紹。

    做為陣列

    陣列 (array) 是一種以數字為索引的線性資料結構,在 Lua 中是以 table 模擬。

    以索引取值

    我們也可以用數字做為索引取值:

    -- Create an array.
    local arr = {"a", "b", "c", "d", "e"}
    
    -- Retrieve the 3rd element.
    assert(arr[3] == "c")

    要注意的是,Lua 的陣列從 1 開始,和大部分主流語言不同。

    以下的陣列會造成空洞 (holes):

    local arr = {}
    
    arr[1] = "a"
    arr[2] = "b"
    -- arr[3] and arr[4] are nil
    arr[5] = "c"
    arr[6] = "d"

    在 Lua 程式中,空洞會造成一些意想不到的 bug,應避免。

    走訪陣列

    使用 for 迴圈搭配 ipairs 函式可走訪陣列:

    -- Create an array.
    local arr = {"a", "b", "c", "d", "e"}
    
    -- Iterate over the array.
    for i, e in ipairs(arr) do
      print(e)
    end

    也可以用數字為索引來走訪陣列:

    -- Create an array.
    local arr = {"a", "b", "c", "d", "e"}
    
    -- Iterate over the array.
    for i = 1, #arr do
      print(arr[i])
    end

    陣列長度

    Lua 用 # (number sign) 取得陣列長度,見下例:

    -- Create an array.
    local arr = {"a", "b", "c", "d", "e"}
    
    -- Check the length of the array.
    assert(#arr == 5)

    要注意的是,當陣列後端元素皆為 nil 時,不計入長度,見下例:

    local arr = {10, 20, 30, nil, nil}
    assert(#arr == 3)

    插入元素

    利用 table.insert 可在尾端插入新的元素,見以下實例:

    -- Create an array.
    local arr = {"a", "b", "c", "d", "e"}
    
    -- Check the length of the array.
    assert(#arr == 5)
    
    -- Append an element.
    table.insert(arr, "f")
    
    -- Recheck the length of the array.
    assert(#arr == 6)
    assert(arr[6] == "f")

    或是使用 arr[#arr+1] = "f" 也可從尾端插入元素。

    除了從尾端插入元素,也可從中間插入元素,見下例:

    -- Create an array.
    local arr = {"a", "b", "c", "d", "e"}
    
    -- Check the length of the array.
    assert(#arr == 5)
    
    -- Insert an element at position 3.
    table.insert(arr, 3, "f")
    
    -- Recheck the length of the array.
    assert(#arr == 6)
    assert(arr[3] == "f")

    去除元素

    使用 table.remove 可以從尾端去除元素,見下例:

    -- Create an array.
    local arr = {"a", "b", "c", "d", "e"}
    
    -- Check the length of the array.
    assert(#arr == 5)
    
    -- Pop an element.
    popped = table.remove(arr)
    
    -- Recheck the length of the array.
    assert(#arr == 4)
    assert(popped == "e")

    也可以從中間去除元素,見下例:

    -- Create an array.
    local arr = {"a", "b", "c", "d", "e"}
    
    -- Check the length of the array.
    assert(#arr == 5)
    
    -- Remove an element from position 2.
    removed = table.remove(arr, 2)
    
    -- Recheck the length of the array.
    assert(#arr == 4)
    assert(removed == "b")

    做為字典

    字典是一種以鍵/值對 (key-value pairs) 為組合的非線性資料結構,以鍵為索引取值。

    建立字典

    以下實例建立一個字典:

    -- Create an dictionary.
    local dict = {
      one="eins",
      two="zwei",
      three="drei"
    }
    
    -- Retrieve the value with the key.
    assert(dict["one"] == "eins")

    也可以先建立字典再逐一加入值:

    -- Create an dictionary.
    local dict = {}
    
    -- Add some key/value pairs.
    dict["one"] = "eins"
    dict["two"] = "zwei"
    dict["three"] = "drei"
    
    -- Retrieve the value with the key.
    assert(dict["one"] == "eins")

    走訪字典

    可以用 for 迴圈搭配 pairs 函式來走訪字典,見下例:

    -- Create an dictionary.
    local dict = {one="eins", two="zwei", three="drei"}
    
    -- Iterate over the dictionary.
    for k, v in pairs(dict) do
      print(k .. ": " .. v)
    end

    註:pairs 用來走訪字典,ipairs 用來走訪陣列。

    去除元素

    若要去除元素,將其值設為 nil 即可,以下是實例:

    -- Create an dictionary.
    local dict = {one="eins", two="zwei", three="drei"}
    
    -- Remove an key-value pair.
    dict["three"] = nil
    
    -- Retrieve some values.
    assert(dict["one"] == "eins")
    assert(dict["three"] == nil)