此第一版是針對 Lua 5.0 編寫。儘管在很大程度上仍然適用於後續版本,但仍有一些差異。
第四版針對 Lua 5.3,可在 Amazon 和其他書店購買。
購買本書,您也將協助 支援 Lua 專案


28.1 – 使用者資料

我們首先關注的是如何在 Lua 中表示陣列值。Lua 提供了一個專門針對此目的的基本類型:使用者資料。使用者資料在 Lua 中提供一個原始記憶體區域,不含任何預定義的運算。

Lua API 提供下列函式來建立使用者資料

    void *lua_newuserdata (lua_State *L, size_t size);
lua_newuserdata 函式會配置一個具有指定大小的記憶體區塊,將對應的使用者資料推入堆疊,並傳回區塊位址。如果您因某些原因需要透過其他方式配置記憶體,則可以很輕鬆地建立一個大小為指標的使用者資料,並將一個指向實際記憶體區塊的指標儲存在其中。我們將在下一章中看到此技術的範例。

使用 lua_newuserdata,建立新陣列的函式如下

    static int newarray (lua_State *L) {
      int n = luaL_checkint(L, 1);
      size_t nbytes = sizeof(NumArray) + (n - 1)*sizeof(double);
      NumArray *a = (NumArray *)lua_newuserdata(L, nbytes);
      a->size = n;
      return 1;  /* new userdatum is already on the stack */
    }
(luaL_checkint 函式是 luaL_checknumber 的整數變體。)一旦 newarray 在 Lua 中註冊,您就可以使用類似 a = array.new(1000) 的陳述式建立新陣列。

若要儲存一個項目,我們將使用類似 array.set(array, index, value) 的呼叫。稍後我們將看到如何使用元表來支援更傳統的語法 array[index] = value。對於這兩種表示法,底層函式都是相同的。它假設索引從 1 開始,如同 Lua 中常見的。

    static int setarray (lua_State *L) {
      NumArray *a = (NumArray *)lua_touserdata(L, 1);
      int index = luaL_checkint(L, 2);
      double value = luaL_checknumber(L, 3);
    
      luaL_argcheck(L, a != NULL, 1, "`array' expected");
    
      luaL_argcheck(L, 1 <= index && index <= a->size, 2,
                       "index out of range");
    
      a->values[index-1] = value;
      return 0;
    }
luaL_argcheck 函式會檢查指定的條件,並在必要時引發錯誤。因此,如果我們使用錯誤的引數呼叫 setarray,我們會收到一個說明性的錯誤訊息
    array.set(a, 11, 0)
    --> stdin:1: bad argument #1 to `set' (`array' expected)

下一個函式會擷取一個項目

    static int getarray (lua_State *L) {
      NumArray *a = (NumArray *)lua_touserdata(L, 1);
      int index = luaL_checkint(L, 2);
    
      luaL_argcheck(L, a != NULL, 1, "`array' expected");
    
      luaL_argcheck(L, 1 <= index && index <= a->size, 2,
                       "index out of range");
    
      lua_pushnumber(L, a->values[index-1]);
      return 1;
    }
我們定義另一個函式來擷取陣列的大小
    static int getsize (lua_State *L) {
      NumArray *a = (NumArray *)lua_touserdata(L, 1);
      luaL_argcheck(L, a != NULL, 1, "`array' expected");
      lua_pushnumber(L, a->size);
      return 1;
    }
最後,我們需要一些額外的程式碼來初始化我們的函式庫
    static const struct luaL_reg arraylib [] = {
      {"new", newarray},
      {"set", setarray},
      {"get", getarray},
      {"size", getsize},
      {NULL, NULL}
    };
    
    int luaopen_array (lua_State *L) {
      luaL_openlib(L, "array", arraylib, 0);
      return 1;
    }
我們再次使用輔助函式庫中的 luaL_openlib。它會建立一個具有指定名稱的表格(在我們的範例中為 "array"),並使用 arraylib 陣列指定的成對名稱函式填入表格。

開啟函式庫後,我們就可以在 Lua 中使用我們的類型

    a = array.new(1000)
    print(a)               --> userdata: 0x8064d48
    print(array.size(a))   --> 1000
    for i=1,1000 do
      array.set(a, i, 1/i)
    end
    print(array.get(a, 10))  --> 0.1

在 Pentium/Linux 上執行此實作時,一個具有 100K 元素的陣列會佔用 800 KB 的記憶體,正如預期;一個等效的 Lua 表格需要超過 1.5 MB。