lua之诡异的#

lua中的#是获取table中有序序列的个数。例如:

local t = {a = 1, 1,3,4,5,6}
print(#t)--5

在table的实现中,有两种方式保存元素,一种 数组,一种hash散列表。#就是获取table中的数组元素个数。例如上面代码就是获取数组的个数。其中a = 1保存在散列表中。如果在table中增加几个nil,会有什么问题呢?

local t = {a = 1, 1,3,nil,4,5,6,nil}
print(#t)--2
是不是相当诡异?为什么会这样?我们从源码一步步开剥开迷雾。

const TValue *luaH_getint (Table *t, lua_Integer key) {
  /* (1 <= key && key <= t->sizearray) */
  if (l_castS2U(key) - 1 < t->sizearray)
    return &t->array[key - 1];
  else {
    Node *n = hashint(t, key);
    for (;;) {  /* check whether 'key' is somewhere in the chain */
      if (ttisinteger(gkey(n)) && ivalue(gkey(n)) == key)
        return gval(n);  /* that's it */
      else {
        int nx = gnext(n);
        if (nx == 0) break;
        n += nx;
      }
    }
    return luaO_nilobject;
  }
}
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))) {//获取数组最后一个元素是否为nil,
    i = j;
    if (j > cast(unsigned int, MAX_INT)/2) {  /*防止溢出,只有j > MAX_INT/2条件才为真。说明table是一个大的table */
      /* table was built with bad purposes: resort to linear search */
      i = 1;
      while (!ttisnil(luaH_getint(t, i))) i++;//当数组中出现值为nil,就终止循环。
      return i - 1;
    }
    j *= 2;
  }
  /* 使用二分法搜索。进过上面的while循环后,i = j, j = 原来的2倍。m = (i + j)/2 我们可以理解成m = (j + 2j)/2,第一次二分法查找,m = j + floor(j/2),此时m > j(大于table数组个数),ttisnil一定为nil,j = m.第二次二分法查找,此时i= j的,所以 m = (j + floor(j/2) + j)/2 等效于 m = j + floor(j/4),任然大于table数组的个数,ttisnil一定为nil,j = m。经过几次这样的操作,m就等于数组的长度了*/
while (j - i > 1) {
    unsigned int m = (i+j)/2;
    if (ttisnil(luaH_getint(t, m))) j = m;
    else i = m;
  }
  return i;
}



</pre><pre code_snippet_id="1762411" snippet_file_name="blog_20160714_8_1502282" name="code" class="cpp">int luaH_getn (Table *t) {
  unsigned int j = t->sizearray;//获取table中数组部分元素个数
  if (j > 0 && ttisnil(&t->array[j - 1])) {//元素个数大于0且数组最后一个元素nil,就使用二分法进行查找。*上面lua代码结果诡异的原因。
    /* 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散列表,直接返回数组的大小 */
    return j;  /* that is easy... */
  else return unbound_search(t, j);/*正常搜索*/
}

二分法介绍:点击我

诡异的原因知道了,就要尽量避免在table的有序数组最后插入nil值,如果二分法中间位m,恰巧m-1也是nil就会有诡异的结果。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值