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