第一版是為 Lua 5.0 所寫。儘管對後續版本仍有很大的關聯性,但有一些差異。
第四版以 Lua 5.3 為目標,可以在 Amazon 和其他書店取得。
購買這本書,您也可以協助 支援 Lua 專案


14.1 – 使用動態名稱存取全域變數

通常,指派就足以取得和設定全域變數。不過,我們常常需要某種形式的元程式設計,例如當我們需要處理一個全域變數時,其名稱儲存在另一個變數中,或在執行時以某種方式計算出來。為了取得這個變數的值,許多程式設計師會忍不住寫出類似下列的程式碼

    loadstring("value = " .. varname)()
    value = loadstring("return " .. varname)()
例如,如果 varnamex,連接會產生 "return x"(或 "value = x",使用第一個形式),執行時會達成預期的結果。不過,此類程式碼會建立和編譯新的區塊,並產生大量額外的處理工作。您可以使用下列程式碼達成相同的效果,其效率比前一個程式碼高出一個數量級以上
    value = _G[varname]
由於環境是一個正規的表格,您可以簡單地使用想要的鍵(變數名稱)為其建立索引。

以類似的方式,您可以指派給一個以動態方式計算出來的名稱的全域變數,寫成 _G[varname] = value。不過要小心:有些程式設計師會對這些函式過於興奮,最後寫出類似 _G["a"] = _G["var1"] 的程式碼,這只不過是寫 a = var1 的複雜方式。



前一個問題的一般化是允許動態名稱中的欄位,例如 "io.read""a.b.c.d"。我們使用一個迴圈來解決這個問題,從 _G 開始,並逐個欄位演進

    function getfield (f)
      local v = _G    -- start with the table of globals
      for w in string.gfind(f, "[%w_]+") do
        v = v[w]
      end
      return v
    end
我們仰賴 string 函式庫中的 gfind 來反覆處理 f 中的所有字詞(其中「字詞」是一個或多個字母數字字元和底線的序列)。

設定欄位的對應函式稍微複雜一點。像下列這樣的指派

    a.b.c.d.e = v
完全等同於
    local temp = a.b.c.d
    temp.e = v
換句話說,我們必須擷取到最後一個欄位名稱;我們必須個別處理最後一個欄位。新的 setfield 函式也會在路徑中建立中間的表格,如果它們不存在的話
    function setfield (f, v)
      local t = _G    -- start with the table of globals
      for w, d in string.gfind(f, "([%w_]+)(.?)") do
        if d == "." then      -- not last field?
          t[w] = t[w] or {}   -- create table if absent
          t = t[w]            -- get the table
        else                  -- last field
          t[w] = v            -- do the assignment
        end
      end
    end
這個新的模式會在變數 w 中擷取欄位名稱,以及在變數 d 中擷取一個可選的後續點。如果欄位名稱後面沒有點,那麼它就是最後一個名稱。(我們將在 第 20 章 中詳細討論模式比對。)

使用先前的函式,呼叫

    setfield("t.x.y", 10)
會建立一個全域表格 t、另一個表格 t.x,並將 10 指定給 t.x.y
    print(t.x.y)     --> 10
    print(getfield("t.x.y"))   --> 10