第一版是針對 Lua 5.0 編寫的。雖然對於後續版本來說仍然有很大關聯,但有一些不同之處。
第四版針對 Lua 5.3,可在 Amazon 和其他書店購買。
購買本書,您還可以幫助 支援 Lua 專案


2.5 – 表格

表格類型實作關聯陣列。關聯陣列是一種陣列,不僅可以用數字索引,還可以字串或語言中的任何其他值(nil 除外)索引。此外,表格沒有固定大小;您可以動態地向表格中新增任意數量的元素。表格是 Lua 中的主要(事實上是唯一的)資料結構化機制,而且功能強大。我們使用表格以簡單、統一且有效率的方式表示一般陣列、符號表、集合、記錄、佇列和其他資料結構。Lua 也使用表格來表示套件。當我們撰寫 io.read 時,我們的用意是「io 套件中的 read 項目」。對於 Lua 來說,這表示「使用字串 "read" 作為鍵值,索引表格 io」。

Lua 中的表格既不是值也不是變數;它們是物件。如果您熟悉 Java 或 Scheme 中的陣列,那麼您對我們的用意應該有相當程度的了解。但是,如果您對陣列的概念來自 C 或 Pascal,那麼您必須稍微轉換一下思維。您可以將表格視為動態配置的物件;您的程式只會處理它們的參考(或指標)。幕後沒有隱藏的複本或新表格的建立。此外,您不必在 Lua 中宣告表格;事實上,沒有辦法宣告表格。您透過建構式運算式建立表格,其最簡單的形式寫作 {}

    a = {}     -- create a table and store its reference in `a'
    k = "x"
    a[k] = 10        -- new entry, with key="x" and value=10
    a[20] = "great"  -- new entry, with key=20 and value="great"
    print(a["x"])    --> 10
    k = 20
    print(a[k])      --> "great"
    a["x"] = a["x"] + 1     -- increments entry "x"
    print(a["x"])    --> 11
表格總是匿名的。持有表格的變數與表格本身之間沒有固定的關係
    a = {}
    a["x"] = 10
    b = a      -- `b' refers to the same table as `a'
    print(b["x"])  --> 10
    b["x"] = 20
    print(a["x"])  --> 20
    a = nil    -- now only `b' still refers to the table
    b = nil    -- now there are no references left to the table
當程式中沒有任何表格的參考時,Lua 記憶體管理最終會刪除表格並重複使用其記憶體。

每個表格都可以儲存具有不同類型索引的數值,而且會根據需要容納新項目而擴充

    a = {}     -- empty table
    -- create 1000 new entries
    for i=1,1000 do a[i] = i*2 end
    print(a[9])    --> 18
    a["x"] = 10
    print(a["x"])  --> 10
    print(a["y"])  --> nil
請注意最後一行:與全域變數一樣,如果表格欄位未初始化,則會評估為 nil。也與全域變數一樣,您可以將 nil 指定給表格欄位以刪除它。這並非巧合:Lua 將全域變數儲存在一般表格中。有關此主題的更多資訊,請參閱 第 14 章

若要表示記錄,請使用欄位名稱作為索引。Lua 提供 a.name 作為 a["name"] 的語法糖,支援這種表示方式。因此,我們可以更簡潔地撰寫前一個範例的最後幾行,如下所示

    a.x = 10                    -- same as a["x"] = 10
    print(a.x)                  -- same as print(a["x"])
    print(a.y)                  -- same as print(a["y"])
對於 Lua 來說,這兩種形式是等效的,可以自由地相互替換;但對於人類讀者來說,每種形式可能表示不同的意圖。

初學者常犯的錯誤是將 a.xa[x] 混淆。第一種形式代表 a["x"],也就是由字串 "x" 索引的表格。第二種形式是由變數 x 的值索引的表格。請看差異

    a = {}
    x = "y"
    a[x] = 10                 -- put 10 in field "y"
    print(a[x])   --> 10      -- value of field "y"
    print(a.x)    --> nil     -- value of field "x" (undefined)
    print(a.y)    --> 10      -- value of field "y"

要表示一個傳統陣列,您只需使用具有整數鍵的表格。沒有辦法宣告它的尺寸;您只需初始化您需要的元素

    -- read 10 lines storing them in a table
    a = {}
    for i=1,10 do
      a[i] = io.read()
    end
當您迭代陣列的元素時,第一個未初始化的索引將導致 nil;您可以使用這個值作為哨兵來表示陣列的結束。例如,您可以使用以下程式碼列印上一個範例中讀取的行
    -- print the lines
    for i,line in ipairs(a) do
      print(line)
    end
Lua 基本函式庫提供 ipairs,這是一個方便的函式,讓您可以迭代陣列的元素,並遵循陣列在第一個 nil 元素結束的慣例。

由於您可以使用任何值索引表格,因此您可以從任何您喜歡的數字開始陣列的索引。然而,在 Lua 中慣例是從一(而不是像 C 中的零)開始陣列,而標準函式庫也遵循此慣例。

由於我們可以用任何類型索引表格,因此在索引表格時,我們會遇到與等式中出現的相同細微差別。雖然我們可以用數字 0 和字串 "0" 索引表格,但這兩個值是不同的(根據等式),因此表示表格中的不同位置。同樣地,字串 "+1""01""1" 都表示不同的位置。當對索引的實際類型有疑問時,請使用明確轉換以確保

    i = 10; j = "10"; k = "+10"
    a = {}
    a[i] = "one value"
    a[j] = "another value"
    a[k] = "yet another value"
    print(a[j])            --> another value
    print(a[k])            --> yet another value
    print(a[tonumber(j)])  --> one value
    print(a[tonumber(k)])  --> one value
如果您不注意這一點,您可能會在程式中引入細微的錯誤。