This first edition was written for Lua 5.0. While still largely relevant for later versions, there are some differences.
The fourth edition targets Lua 5.3 and is available at Amazon and other bookstores.
By buying the book, you also help to support the Lua project.


27.1 – Array Manipulation

"Array", in Lua, is just a name for a table used in a specific way. We can manipulate arrays using the same functions we use to manipulate tables, namely lua_settable and lua_gettable. However, contrary to the general philosophy of Lua, economy and simplicity, the API provides special functions for array manipulation. The reason for that is performance: Frequently we have an array access operation inside the inner loop of an algorithm (e.g., sorting), so that any performance gain in this operation can have a big impact on the overall performance of the function.

The functions that the API provides for array manipulation are

    void lua_rawgeti (lua_State *L, int index, int key);
    void lua_rawseti (lua_State *L, int index, int key);
The description of lua_rawgeti and lua_rawseti is a little confusing, as it involves two indices: index refers to where the table is in the stack; key refers to where the element is in the table. The call lua_rawgeti(L, t, key) is equivalent to the sequence
    lua_pushnumber(L, key);
    lua_rawget(L, t);
when t is positive (otherwise, you must compensate for the new item in the stack). The call lua_rawseti(L, t, key) (again for t positive) is equivalent to
    lua_pushnumber(L, key);
    lua_insert(L, -2);  /* put `key' below previous value */
    lua_rawset(L, t);
Note that both functions use raw operations. They are faster and, anyway, tables used as arrays seldom use metamethods.

As a concrete example of the use of these functions, we could rewrite the loop body from our previous l_dir function from

        lua_pushnumber(L, i++);  /* key */
        lua_pushstring(L, entry->d_name);  /* value */
        lua_settable(L, -3);
to
        lua_pushstring(L, entry->d_name);  /* value */
        lua_rawseti(L, -2, i++);  /* set table at key `i' */

As a more complete example, the following code implements the map function: It applies a given function to all elements of an array, replacing each element by the result of the call.

    int l_map (lua_State *L) {
      int i, n;
    
      /* 1st argument must be a table (t) */
      luaL_checktype(L, 1, LUA_TTABLE);
    
      /* 2nd argument must be a function (f) */
      luaL_checktype(L, 2, LUA_TFUNCTION);
    
      n = luaL_getn(L, 1);  /* get size of table */
    
      for (i=1; i<=n; i++) {
        lua_pushvalue(L, 2);   /* push f */
        lua_rawgeti(L, 1, i);  /* push t[i] */
        lua_call(L, 1, 1);     /* call f(t[i]) */
        lua_rawseti(L, 1, i);  /* t[i] = result */
      }
    
      return 0;  /* no results */
    }
This example introduces three new functions. The luaL_checktype function (from lauxlib.h) ensures that a given argument has a given type; otherwise, it raises an error. The luaL_getn function gets the size of the array at the given index (table.getn calls luaL_getn to do its job). The lua_call function does an unprotected call. It is similar to lua_pcall, but in case of errors it throws the error, instead of returning an error code. When you are writing the main code in an application, you should not use lua_call, because you want to catch any errors. When you are writing functions, however, it is usually a good idea to use lua_call; if there is an error, just leave it to someone that cares about it.