unpack的一点使用问题

  • 现象

lua支持不定数量参数,通过...传送,使用unpack解开。看一段代码

 1 local function arg2(...)
 2     print('****************')
 3     print(unpack(arg))
 4     for k, v in pairs(arg) do
 5         print(k, v)
 6     end
 7     print(arg[1], arg[2], arg[3])
 8     print('++++++++++++++++')
 9 end
10 arg2('a', nil, 'c')
11 arg2(nil, 'b', 'c')
12 arg2(nil, 'b', nil)

其实我们预期传入三个参数,但是由于某种情况,有的参数值为nil,我们当然希望在unpack后还和传入的值一样。看一下执行结果

我们发现,前面两个和预期一致,第三个unpack却没有得到内容,是个空串,但按下标逐个打印的时候内容仍然是存在的,这样势必会导致函数执行结果不能和传入的参数一致。

  • 解释

unpack的说明如下,从指定的范围依次取出,如果不指定则从开始1到长度l,由求长度操作符#决定。

上面的代码中没有给出i和j,使用默认处理。换为指定范围如下:

 1 local function arg2(...)
 2     print('****************')
 3     print(unpack(arg, 1, 3))
 4     for k, v in pairs(arg) do
 5         print(k, v)
 6     end
 7     print(arg[1], arg[2], arg[3])
 8     print('++++++++++++++++')
 9 end
10 arg2('a', nil, 'c')
11 arg2(nil, 'b', 'c')
12 arg2(nil, 'b', nil)

执行后如下

 这时候就和预期的一致了。不幸的是,我们并不总是知道这个长度的。而未指定时结果不对,猜想和#操作的结果有关,其文档说明是

关键是后面补充的部分,如果第一个元素是nil,则长度为0;如果非nil值之间有了“洞”,则不确定长度了。

  • 代码

下面是unpack的代码

 1 static int luaB_unpack (lua_State *L) {
 2   int i, e, n;
 3   luaL_checktype(L, 1, LUA_TTABLE);
 4   i = luaL_optint(L, 2, 1);
 5   e = luaL_opt(L, luaL_checkint, 3, luaL_getn(L, 1));
 6   n = e - i + 1;  /* number of elements */
 7   if (n <= 0) return 0;  /* empty range */
 8   luaL_checkstack(L, n, "table too big to unpack");
 9   for (; i<=e; i++)  /* push arg[i...e] */
10     lua_rawgeti(L, 1, i);
11   return n;
12 }

 下面是求解#的过程

 1 static int unbound_search (Table *t, unsigned int j) {
 2   unsigned int i = j;  /* i is zero or a present index */
 3   j++;
 4   /* find `i' and `j' such that i is present and j is not */
 5   while (!ttisnil(luaH_getnum(t, j))) {
 6     i = j;
 7     j *= 2;
 8     if (j > cast(unsigned int, MAX_INT)) {  /* overflow? */
 9       /* table was built with bad purposes: resort to linear search */
10       i = 1;
11       while (!ttisnil(luaH_getnum(t, i))) i++;
12       return i - 1;
13     }
14   }
15   /* now do a binary search between them */
16   while (j - i > 1) {
17     unsigned int m = (i+j)/2;
18     if (ttisnil(luaH_getnum(t, m))) j = m;
19     else i = m;
20   }
21   return i;
22 }
23 
24 
25 /*
26 ** Try to find a boundary in table `t'. A `boundary' is an integer index
27 ** such that t[i] is non-nil and t[i+1] is nil (and 0 if t[1] is nil).
28 */
29 int luaH_getn (Table *t) {
30   unsigned int j = t->sizearray;
31   if (j > 0 && ttisnil(&t->array[j - 1])) {
32     /* there is a boundary in the array part: (binary) search for it */
33     unsigned int i = 0;
34     while (j - i > 1) {
35       unsigned int m = (i+j)/2;
36       if (ttisnil(&t->array[m - 1])) j = m;
37       else i = m;
38     }
39     return i;
40   }
41   /* else must find a boundary in hash part */
42   else if (t->node == dummynode)  /* hash part is empty? */
43     return j;  /* that is easy... */
44   else return unbound_search(t, j);
45 }

 

posted on 2013-01-27 20:18  小交响曲  阅读(4586)  评论(0编辑  收藏  举报

导航