27.1 数组操作 |
Lua中数组实际上就是以特殊方式使用的table的别名。我们可以使用任何操纵table的函数来对数组操作,即lua_settable和lua_gettable。然而,与Lua常规简洁思想(economy and simplicity)相反的是,API为数组操作提供了一些特殊的函数。这样做的原因出于性能的考虑:因为我们经常在一个算法(比如排序)的循环的内层访问数组,所以这种内层操作的性能的提高会对整体的性能的改善有很大的影响。
API提供了下面两个数组操作函数:
void lua_rawgeti (lua_State *L, int index, int key);
void lua_rawseti (lua_State *L, int index, int key);
关于的lua_rawgeti和lua_rawseti的描述有些使人糊涂,因为它涉及到两个索引:index指向table在栈中的位置;key指向元素在table中的位置。当t使用负索引的时候(otherwise,you must compensate for the new item in the stack),调用lua_rawgeti(L,t,key)等价于:
lua_pushnumber(L, key);
lua_rawget(L, t);
调用lua_rawseti(L, t, key)(也要求t使用负索引)等价于:
lua_pushnumber(L, key);
lua_insert(L, -2); /* put 'key' below previous value */
lua_rawset(L, t);
注意这两个寒暑都是用raw操作,他们的速度较快,总之,用作数组的table很少使用metamethods。
下面看如何使用这些函数的具体的例子,我们将前面的l_dir函数的循环体:
lua_pushnumber(L, i++); /* key */
lua_pushstring(L, entry->d_name); /* value */
lua_settable(L, -3);
改写为:
lua_pushstring(L, entry->d_name); /* value */
lua_rawseti(L, -2, i++); /* set table at key 'i' */
下面是一个更完整的例子,下面的代码实现了map函数:以数组的每一个元素为参数调用一个指定的函数,并将数组的该元素替换为调用函数返回的结果。
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 */
}
这里面引入了三个新的函数。luaL_checktype(在lauxlib.h中定义)用来检查给定的参数有指定的类型;否则抛出错误。luaL_getn函数栈中指定位置的数组的大小(table.getn是调用luaL_getn来完成工作的)。lua_call的运行是无保护的,他与lua_pcall相似,但是在错误发生的时候她抛出错误而不是返回错误代码。当你在应用程序中写主流程的代码时,不应该使用lua_call,因为你应该捕捉任何可能发生的错误。当你写一个函数的代码时,使用lua_call是比较好的想法,如果有错误发生,把错误留给关心她的人去处理。