lua table的長度問題

來源:互聯網
上載者:User


細談一下lua裡很多人有疑問的table長度問題。

<pre name="code" class="cpp">1.> tbl = {1,2,3}  > print(#tbl)3> 2.> tbl = {1,nil,3}> print(#tbl)    3> 3.> tbl = {1,nil,3,nil}> print(#tbl)      1> 


 

情況1正常,情況2有點不正常,情況3很不正常,好,先上源碼。

int luaH_getn (Table *t) {  unsigned int j = t->sizearray;  if (j > 0 && ttisnil(&t->array[j - 1])) {    /* there is a boundary in the array part: (binary) search for it */    unsigned int i = 0;    while (j - i > 1) {      unsigned int m = (i+j)/2;      if (ttisnil(&t->array[m - 1])) j = m;      else i = m;    }    return i;  }  /* else must find a boundary in hash part */  else if (isdummy(t->node))  /* hash part is empty? */    return j;  /* that is easy... */  else return unbound_search(t, j);}

此函數是用來求知table的長度,理解這個函數對於lua table的整個原理有很大協助。

條件1:j>0而且數組部分最後一個為空白,則進入下面的二分尋找,此二分尋找就是找到一個i不為空白,j為空白的的兩個索引,當j-i>1就跳出來返回i.

條件2:j<=0或者數組最後一個不為空白,而且沒有hash部分,此時,沒辦法,沒得統計,折中返回數組的長度的。

條件3:j<=0或者數組最後一個不為空白,有hash部分,進入到unbound_search,從字面意思,就是在一個亂的區間尋找,此函數最終也是一個二分尋找。

先理解條件1,最上面的情況三就符合條件1的情形,好,直接上gdb.下面的截圖,慢慢看。

數組長度j = t->t->sizearray = 4,j>0,把最後一個元素列印出來:

tt_ = 0表示此元素為nil值,符合條件1,進入到二分尋找,此二分尋找就是找到一個左邊不為nil,右邊為nil時的左邊索引。

i = 0,j = 4,m = (i+j)/2 = 2,m-1 = 1,索引為1的元索是nil,0才是1,所以j = m = 2;

i = 0,j = 2,m = (i+j)/2 = 1,m-1 = 0,索引為0的元索是1,所以i = m = 1;

i = 1,j=2,退出迴圈,長度為1.

 

可以得知,就算僅僅統計只有數組部分的長度也有可能不正常。

條件2製造很簡單,跟情況2符合,看gdb:

tbl = {1,nil,3},此table度度j = t->sizearray = 3,最後一個元素tt_ = 3,n = 3,就表示此value為number類型,值為3,也就是tbl最後一個元素不為nil,t->node 而且isdummy,hash部分也為空白,折中直接返回數組長度3.

先看unbound_search函數實現:

static int unbound_search (Table *t, unsigned int j) {  unsigned int i = j;  /* i is zero or a present index */  j++;  /* find `i' and `j' such that i is present and j is not */  while (!ttisnil(luaH_getint(t, j))) {    i = j;    j *= 2;    if (j > cast(unsigned int, MAX_INT)) {  /* overflow? */      /* table was built with bad purposes: resort to linear search */      i = 1;      while (!ttisnil(luaH_getint(t, i))) i++;      return i - 1;    }  }  /* now do a binary search between them */  while (j - i > 1) {    unsigned int m = (i+j)/2;    if (ttisnil(luaH_getint(t, m))) j = m;    else i = m;  }  return i;}const TValue *luaH_getint (Table *t, int key) {  /* (1 <= key && key <= t->sizearray) */  if (cast(unsigned int, key-1) < cast(unsigned int, t->sizearray))    return &t->array[key-1];  else {    lua_Number nk = cast_num(key);    Node *n = hashnum(t, nk);    do {  /* check whether `key' is somewhere in the chain */      if (ttisnumber(gkey(n)) && luai_numeq(nvalue(gkey(n)), nk))        return gval(n);  /* that's it */      else n = gnext(n);    } while (n);    return luaO_nilobject;  }}

此函數傳入一個數組長度,也就是認為數組滿了,才進入到此函數。

第一個while就是一直尋找到一個j,而且以此j為key,不存在於table,包括數組部分和hash部分(luaH_getint的實現),側退出第一個while,如果中途j>MAX_INT,沒辦法,此j已經好大了,只能resort to linear search了,就是轉到線性尋找,一個一個找到一個為空白就返回I-1為table的長度。

 在第一個while找到j之後,此時i和j就是左邊不為nil,右邊為nil的兩個值,在此兩值之前再二分尋找,尋找到一個最小的左邊不為nil,右邊為nil的兩個值,返回前者。

 好,製造此情況,看gdb:

tbl = {1,nil,3,qs = 19891103},數組長度為3,最後一個元素不為nil,


有hash部分,見qs = 19891103,進入到一個雜亂無章的區間內尋找(unbound_search),在進入第一個while時i=3,j=4,luaH_getint的時候

,以key為4在數組部分和hash部分都不存在的。直接進入到以左邊為3,右邊為4的二分尋找(此二分尋找上面有說)。直接就返回3為table的長度。

可見,進入到unbound_search的時候,此時獲得資料長度,有可能靠譜,大部分情況都是不靠譜的。

如果數組部分是滿的而且沒有空洞情況,而且存在於hash部分的key跟數組部分是連續起來的,此時擷取長度是達到預期的,有hash部分的時候就要進入到unbound_search尋找,假如tbl = {1,2,3,[4] = 4},此時,對外來看,此tbl確實是一個數組,但在記憶體裡,並不是,1,2,3在數組部分,4在hash部分,所以在進入unbound_search,此時擷取長度也是正常的.


聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.