-
现象
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 }