lua中#取table长度的一些坑以及如何改良

lua中#取table长度的一些坑以及如何改良

  • 【前言】
      • 废话不多说,直接看个例子:
  • 【#底层源码分析】
      • #代表取长度。对字符串来说,取字符串的长度, 对table来说取长度会复杂一些。
  • 【#对table取长度换成pairs遍历表】

【前言】

废话不多说,直接看个例子:

local tab = {a = 1, 2,3,4}
local tab1 = {1,2}
print(#tab,#tab1)-->3	2

这个print输出结果中#tab=3,明明有4个元素,取的长度只等于3,这是我在工作中遇到的bug,接下来会分析源码,并将改良方法一并奉上。

【#底层源码分析】

#代表取长度。对字符串来说,取字符串的长度, 对table来说取长度会复杂一些。

lua中的table可以用数字或字符串等作为key(例[“str”]), #号返回在表’t’中最开始的连续部分的整数索引(即i为整数,t[i]非nil,且t[i+1]为nil),如果t[1]为nil则为0,即使t[5], t[6], t[7]是存在的,#t仍然为零。

可能看代码更容易理解。

/*
** Try to find a boundary in table 't'. A 'boundary' is an integer index
** such that t[i] is non-nil and t[i+1] is nil (and 0 if t[1] is nil).
*/
lua_Unsigned 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))  /* hash part is empty? */
    return j;  /* that is easy... */
  else return unbound_search(t, j);
}

这个是lua5.3中求table长度的方法, 函数中t->array是一个数组,用二分法找到一个索引i,使得t[i]存在而t[i+1]为nil,就把i作为table的长度返回。
luaH_getn方法先检查先检查数组边界,数组没有就检查hash部分的边界,也就是上面源码中,会先遍历数组部分,数组部分有就结束,没有再遍历hash表部分,再来看下关于hash表部分的取长度。

static lua_Unsigned unbound_search (Table *t, lua_Unsigned j) {
  lua_Unsigned 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;
    if (j > l_castS2U(LUA_MAXINTEGER) / 2) {  /* overflow? */
      /* table was built with bad purposes: resort to linear search */
      i = 1;
      while (!ttisnil(luaH_getint(t, i))) i++;
      return i - 1;
    }
    j *= 2;
  }
  /* now do a binary search between them */
  while (j - i > 1) {
    lua_Unsigned m = (i+j)/2;
    if (ttisnil(luaH_getint(t, m))) j = m;
    else i = m;
  }
  return i;
}

所以lua中取table的长度并不表示元素的数量,只有可以肯定table中的元素是用从1开始的连续的整数作为key时才可以得到正确的结果。比如创建一个列表时, 通过 t[#t +1] = new_node 在结尾添加一个元素,下面我们将用pairs遍历表的方式去得到table的长度。

【#对table取长度换成pairs遍历表】

function GetTalbleMapLen(tab)
    local len = 0
    for _, v in pairs(tab) do        
        if v then len = len+1 end        
    end
    return len
end
local tab = {a = 1, 2,3,4}
print(GetTalbleMapLen(tab))-->4

你可能感兴趣的:(脚本语言,lua)