Lua5.2.3源码阅读--Table(ipairs,pairs)
来源:互联网 发布:深圳舞蹈工作室 知乎 编辑:程序博客网 时间:2024/05/16 15:44
这篇文章不错,解决了困扰我的问题,转过来纪念一下
“#”lua表长度计算
local a = {}; a[1] = 10; a[2] = 20; a[3] = 30; a[4] = 40; --a[6] = 60; a[7] = 70; print(#a); -->4 local a = {}; a[1] = 10; a[2] = 20; a[3] = 30; a[4] = 40; a[6] = 60; a[7] = 70; print(#a); -->7
这里就比较奇怪了,得到的不是table的长度。Lua文档中说,如果是一个连续的序列,那么才可以用#计算table的长度,如果中间有nil值,计算将会有问题。那就到源码中找找答案。#计算长度调用的是lua_rawlen函数,该函数如下。
LUA_API size_t lua_rawlen (lua_State *L, int idx) { StkId o = index2addr(L, idx); switch (ttypenv(o)) { case LUA_TSTRING: return tsvalue(o)->len; case LUA_TUSERDATA: return uvalue(o)->len; case LUA_TTABLE: return luaH_getn(hvalue(o)); default: return 0; }}
如果是table类型,调用luaH_getn函数,函数如下。
/*** 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).*/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);}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;}
(1)如果数组部分长度为0,那么返回值为0。
(2)如果数组满,返回sizearray
(3)如果数组有值,且array[sizearray - 1]不为nil,那么按照二分查找,找到第一个有非nil到nil的位置,从而返回数组部分的大小。如果数组部分从1到n不存在的nil,那么这个二分查找的过程是稳定,大小就是table的大小。如果其中存在nil值,那这个二分查找的过程不稳定,得到的值随nil分布的不同而不同。说明在脚本中要慎用”#”去table的长度,除非确定是连续序列。这里很好的解释了刚才取长度异常的问题。
前面已经分析了table的查找,赋值,下面再来看看table的遍历,遍历的函数是luaH_next。
int luaH_next (lua_State *L, Table *t, StkId key) { int i = findindex(L, t, key); /* find original element */ for (i++; i < t->sizearray; i++) { /* try first array part */ if (!ttisnil(&t->array[i])) { /* a non-nil value? */ setnvalue(key, cast_num(i+1)); setobj2s(L, key+1, &t->array[i]); return 1; } } for (i -= t->sizearray; i < sizenode(t); i++) { /* then hash part */ if (!ttisnil(gval(gnode(t, i)))) { /* a non-nil value? */ setobj2s(L, key, gkey(gnode(t, i))); setobj2s(L, key+1, gval(gnode(t, i))); return 1; } } return 0; /* no more elements */}
通过findindex获得一个位置值,如果是在数组部分,就查找数组部分的下一个不为nil的元素。如果找到,就直接返回下一个值,如果未找到,就到hash部分查找。Hash部分从头部开始查找,不会用里面的next指针,直接采用hash数组,从头到尾找。
Table rehash函数
通过代码查找可以看出,rehash只出现在luaH_newkey,也就是在分配新元素时,空间不够,就会触发rehash操作。Rehash源码如下
static void rehash (lua_State *L, Table *t, const TValue *ek) { int nasize, na; int nums[MAXBITS+1]; /* nums[i] = number of keys with 2^(i-1) < k <= 2^i */ int i; int totaluse; for (i=0; i<=MAXBITS; i++) nums[i] = 0; /* reset counts */ nasize = numusearray(t, nums); /* count keys in array part */ totaluse = nasize; /* all those keys are integer keys */ totaluse += numusehash(t, nums, &nasize); /* count keys in hash part */ /* count extra key */ nasize += countint(ek, nums); /* 新元素知否占据数组部分位置 */ totaluse++; /* compute new size for array part */ na = computesizes(nums, &nasize); /* resize the table to new computed sizes */ luaH_resize(L, t, nasize, totaluse - na);}nums数组用具记录每个段的有效值的个数,段的大小是(2^n,2^(n+1))(n>=0)。numusearray返回数组部分的有效值个数,并填充nums数组。numusehash函数用于计算hash部分有效值的个数,如果其中有key为整数,那么也在nums中记录,第三个参数用于记录总的key为整数的元素个数。countint函数判断新的key是否需要记录在nums中。computesizes这是个关键函数,返回一个int值na,表示总的nasize中,有na个将会放在rehash后的数组中。computesizes代码如下。
static int computesizes (int nums[], int *narray) { int i; int twotoi; /* 2^i */ int a = 0; /* number of elements smaller than 2^i */ int na = 0; /* number of elements to go to array part */ int n = 0; /* optimal size for array part */ for (i = 0, twotoi = 1; twotoi/2 < *narray; i++, twotoi *= 2) { if (nums[i] > 0) { a += nums[i]; if (a > twotoi/2) { /* more than half elements present? */ n = twotoi; /* optimal size (till now) */ na = a; /* all elements smaller than n will go to array part */ } } if (a == *narray) break; /* all elements already counted */ } *narray = n; lua_assert(*narray/2 <= na && na <= *narray); return na;}
判断的关键是,计算到某个段位时,如果有效值的个数大于总个数的一半,就把位置赋给n,个数赋给na。依次遍历整个nums数组,就会得到最大的位置n。根据计算出来的nasize和nhsize,调用luaH_resize重新分配table空间。
最后回顾下ipairs和pairs,前者指访问数组部分,在遇到nil的时候就停止遍历,后面即使有值也不会访问到。Pairs是整体遍历,只有有值,就会访问到。
table总结:在遍历table的时候,不允许向table中插入新的元素,这可能会引起rehash,hash表的修改,导致行为未知。删除table中的元素,直接将该值置为nil。使用#计算数组部分的长度的时候,很容易出错,使用时要谨慎。
原贴:http://blog.csdn.net/murisly/article/details/46885327
- Lua5.2.3源码阅读--Table(ipairs,pairs)
- Lua5.2.3源码阅读(3)-Table(ipairs,pairs)
- Lua5.2.3源码阅读(2)-Table
- Lua5.2.3源码阅读(2)-Table
- lua5.2.3源码阅读笔记
- lua table大小及pairs,ipairs
- Lua5.2.3源码阅读(1)-TValue,TString
- Lua5.2.3源码阅读(1)-TValue,TString
- lua中pairs/ipairs, foreach/foreachi, table自定义排序
- Lua table遍历pairs和ipairs的区别
- Lua5.1 源码注释(一) table.c
- lua5.3.1 源码阅读记录(基础)
- lua5.3源码基础阅读(luaL_openlibs)
- 一、pairs vs ipairs
- 【Lua】pairs 和 ipairs
- ipairs和pairs区别
- Lua:ipairs/pairs
- Lua中table的ipairs和pairs以及function的:和.区别
- linux ping
- VirtualApp原理解析(1)--初始化及注入流程
- 前端-移动端h5常用<meta>属性
- PHP持久问题
- eclipse中编写Java程序出现Resource leak: 'input' is never closed解决方法
- Lua5.2.3源码阅读--Table(ipairs,pairs)
- Retrofit + RxJava + OkHttp 让网络请求变的简单-基础篇
- 几个连接HiveServer的客户端界面工具
- zookeeper 分布式锁种类以及优缺点
- 结构体实现通讯录动态内存版
- mybatis处理xml中SQL语句的大于、小于、大于等于、小于等于
- 如何查看github的html页面显示效果
- 控制执行流程
- H5新增标签