pairs和iparis

pairs和iparis

  • 定义

paris和iparis都用于遍历集合,paris遍历集合中的所有元素,而iparis只遍历数组部分,而且数组部分必须连续。其次,pairs遍历的顺序是不确定的。

  • 问题

两个问题,遍历时插入新元素结果为什么不确定,以及同一个数组,为什么用ipairs遍历就是有序的,用pairs遍历就是无序的。

分析

pairs和ipairs的行为只取决于他们的迭代器函数,pairs的迭代器函数是next,而ipairs的迭代器函数是geti,下边我们将对这两个迭代器函数进行分析。

  • geti
//idx代表传入迭代器的表
//n代表传入迭代器的数组下标
LUA_API int lua_geti (lua_State *L, int idx, lua_Integer n) {
  TValue *t;
  const TValue *slot;
  lua_lock(L);
  t = index2value(L, idx);	//取出表
  if (luaV_fastgeti(L, t, n, slot)) {	//获取到t[n]? slot为传出参数,等于t[n]
    setobj2s(L, L->top, slot);	//设置栈顶为t[n]
  }
  else {
    TValue aux;
    setivalue(&aux, n);
    luaV_finishget(L, t, &aux, L->top, slot);	//否则到元表中找
  }
  api_incr_top(L);	//栈顶加1
  lua_unlock(L);
  return ttype(s2v(L->top - 1));
}

//luaV_fastgeti宏(是表,如果索引在数组部分,直接返回t[n],否则luaH_getint)
#define luaV_fastgeti(L,t,k,slot) \
  (!ttistable(t)  \
   ? (slot = NULL, 0)  /* not a table; 'slot' is NULL and result is 0 */  \
   : (slot = (l_castS2U(k) - 1u < hvalue(t)->alimit) \
              ? &hvalue(t)->array[k - 1] : luaH_getint(hvalue(t), k), \
      !isempty(slot)))  /* result not empty? */
      
//表中找key(英文注释很清楚这里就不翻译了)
const TValue *luaH_getint (Table *t, lua_Integer key) {
  if (l_castS2U(key) - 1u < t->alimit)  /* 'key' in [1, t->alimit]? */
    return &t->array[key - 1];
  else if (!limitequalsasize(t) &&  /* key still may be in the array part? */
           (l_castS2U(key) == t->alimit + 1 ||
            l_castS2U(key) - 1u < luaH_realasize(t))) {
    t->alimit = cast_uint(key);  /* probably '#t' is here now */
    return &t->array[key - 1];
  }
  else {
    Node *n = hashint(t, key);	//找到int在hash部分的位置,然后遍历hash桶链表
    for (;;) {  /* check whether 'key' is somewhere in the chain */
      if (keyisinteger(n) && keyival(n) == key)
        return gval(n);  /* that's it */
      else {
        int nx = gnext(n);
        if (nx == 0) break;
        n += nx;
      }
    }
    return &absentkey;
  }
}
  • next

这个在之前的table分析里面详细说过,这部分就大概看一下。

int luaH_next (lua_State *L, Table *t, StkId key) {
  unsigned int asize = luaH_realasize(t);	//数组部分大小2的幂
  unsigned int i = findindex(L, t, s2v(key), asize);  /* find original key */
  //遍历数组找到第一个非空槽位
  for (; i < asize; i++) {  /* try first array part */
    if (!isempty(&t->array[i])) {  /* a non-empty entry? */
      setivalue(s2v(key), i + 1);
      setobj2s(L, key + 1, &t->array[i]);
      return 1;
    }
  }
   //遍历hash链表
  for (i -= asize; cast_int(i) < sizenode(t); i++) {  /* hash part */
    if (!isempty(gval(gnode(t, i)))) {  /* a non-empty entry? */
      Node *n = gnode(t, i);
      getnodekey(L, s2v(key), n);
      setobj2s(L, key + 1, gval(n));
      return 1;
    }
  }
  return 0;  /* no more elements */
}

结论

在表中,数组和hash部分共存,数字下标连续并不意味着存在数组里,数组空间满首先会填充hash部分,直到hash部分达到阈值或者满才会扩容,所以第一步我们首先要知道,数字下标连续并不会一定放在数组里即可。

然后,ipairs的迭代器是这样的,给出一个数字5,他会找key等于6的元素,先找array[6],然后找hash[6]的桶链表,如果有就返回,没有就结束。所以,ipairs是有序的。而pairs是完全按照数组的顺序和hash链表顺序遍历的,也就是说他先遍历数组,然后从头节点遍历hash部分,所以如果数组部分被hash到hash表里,那就是无序的了。

其次,遍历时插入新元素,如果插入到了hash部分,有可能会插入到当前点后边,可以读出,也可能插入到前边,还有可能发生扩容操作导致resize,最终导致hash表rehash,所以遍历时插入元素的结果时不确定的。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值