目录

  • 1 遍历结果是随机的
  • 2 为什么会是随机的
    • 2.1 简单介绍table
      • 2.1.1 数组部分
      • 2.1.2 散列表部分
    • 2.2 获取key在散列表中的位置
      • 2.2.1 首先介绍几个宏
      • 2.2.2 获取key在散列表中的位置
    • 2.3 key为字符串,获取key(类型为字符串)在散列表中的位置
      • 2.3.1 TString结构体
      • 2.3.2 TString.hash的赋值
    • 2.4 计算G->seed
  • 3 总结

这里不讨论lua中pairs和ipairs的区别。仅仅从lua源码的角度,讨论使用pairs遍历table的时候,遍历结果为什么是随机的。

1 遍历结果是随机的

首先需要弄清楚,这个“遍历结果是随机的”是什么意思?

(1) 假设有 test.lua 文件如下,函数testPairs() 用来遍历输出table s:

function testPairs()s = {a=1,aa=2,b=3,c=4,ab=3,xx=4,ax=5,ay=4,xy=6}for k,v in pairs(s) doprint(k,v)end
endtestPairs()
print('-----------------------------')
testPairs()

代码中执行了两次 testPairs()函数,那么输出结果如何呢:

这不是一模一样吗,哪里随机了?可能是table中元素不够多,再多塞几个元素到table中后,发现结果都是一样的。

(2) 换种方式,更改一下"test.lua"中的代码

function testPairs()s = {a=1,aa=2,b=3,c=4,ab=3,xx=4,ax=5,ay=4,xy=6}for k,v in pairs(s) doprint(k,v)end
endtestPairs()

然后在命令行窗口中前后执行两次” lua test.lua”,结果如下:

这样子做,遍历结果的顺序才是随机的。此处通过表现,先简单下个结论。

使用pairs遍历table时:
如果是同一次lua程序运行期间(同一个global_State下),遍历结果的顺序是不变的(毕竟table中的值都没有变过);
否则,顺序是随机的。

2 为什么会是随机的

2.1 简单介绍table

本文仅涉及到table中的遍历,只讨论table中元素的存储方式,所以不赘述table的相关操作(插入,删除节点等操作)。

除了源码,本文还参考了codedump的《lua设计与实现》(版本5.1.4)

table的定义如下:

与遍历相关的字段为:

可以知道table是由数组和散列表两个部分组成的。

2.1.1 数组部分

这部分比较简单,只需知道对于array[i],key = i + 1, value = array[i]。
所以,key-value中的value存储位置为:
(1) 数组中,当(key为正整数 && 1<= key <= sizearray )
(2) 散列表中,其他值

2.1.2 散列表部分

根据key的类型和值,求得其对应的hash值,然后放到散列表中(当然可能会有冲突,处理冲突的方式可进一步阅读函数 luaH_newkey())

2.2 获取key在散列表中的位置

那么将一个key-value插入到table的散列表中,其对应的位置是怎么计算的呢(此处不考虑有冲突的情况)。恰好,lua 源码中将该功能提取成一个函数:
Static Node *mainposition(const Table *t, const TValue *key)

2.2.1 首先介绍几个宏

(1) sizenode() 求table的散列表大小

(2) lmod() 取模操作,这里的size其实就是(1)中的sizenode(t)

(3) gnode() 取table散列表中序号为i的元素的地址

(4) hashpow2(t, n) 根据n的值,对其进行lmod操作,返回其在散列表中的位置

好的,这里记住hashpow2(t, n)的功能就可以了。

2.2.2 获取key在散列表中的位置

这里代码不多,直接上代码

根据key的类型,选择不同的计算方式:
简单挑选几个重要类型

(1) LUA_TNUMINT 整型


可以知道,针对整型,其实就是对其值求lmod操作。这说明如果key是整数,其在散列表中的位置是不会变的(在前后两次调用lua程序中,table的大小是不变的,array和node的大小也不会变)。

(2)LUA_TNUMFLT 浮点型

这里不展开l_hashfloat() 操作,只需知道,它是用来计算float的hash值即可

通过hashmod(t, n)可以知道,当key是浮点数的时候,其在散列表中的位置是不会变的。

(3) LUA_TSHRSTR 短字符串

这里不展开tsvalue(key),只需知道,它是用来将key从TValue类型转换为TString类型(并不是普通的强制转换,和TValue、GCUion结构有关)。

可以知道,针对短字符串,其实是对其hash值(TString.hash)求lmod操作。TString.hash值的获取后面会详细讲。

(4) LUA_TLNGSTR 长字符串

可以知道,针对长字符串,其实是使用函数 luaS_hashlongstr(TSring *)得到的值,并对其值求lmod操作。函数 luaS_hashlongstr()后面会详细讲解。

根据以上四个类型可以知道,在散列表的长度不变(array和node长度不变)的情况下,对于一般类型(整数,浮点数,boolean等)的key, 其在散列表中的位置是不会变的。可以推测,如果在test.lua中,这样设置table: t = {1,2,3, [100]=100, [200]=200},则其遍历顺序肯定是固定的,不是随机的了。

所以这里面需要进一步探讨的就是短字符串和长字符串了。

2.3 key为字符串,获取key(类型为字符串)在散列表中的位置

2.3.1 TString结构体

先上TString结构,长、短字符串使用的都是这个结构体

只需要注意字段TString.hash,其值在求字符串在散列表中的位置时,起了至关重要的作用。

2.3.2 TString.hash的赋值

首先介绍一下函数luaS_hash(), 用来求一个字符串对应的hash值(这里暂时把函数luaS_hash(str, l, seed)的返回值叫做"字符串str的hash值")

其中str指向的是字符串,l是字符串的长度,seed是一个种子值。
通过函数luaS_hash()可知,只要种子不变,那么字符串对应的hash值当然也是不变的。

在new TString的时候,Tstring.hash值是在createstrobj()函数中进行赋值的,其值为h。

现在,需要找找是哪里调用了 createstrobj(),且参数h是多少?

(1) 新建字符串TString

(2) 短字符串
internshrstr()函数较长,这里折叠一部分无关代码。

(lua源码中经常喜欢用小写字母的l,和数字1有点像,这里替换成size,表示字符串的长度)

可以知道,对于短字符串来说,TString.hash = luaS_hash(str, size, g->seed),其hash值和(str, size, G->seed)有关

(3) 长字符串

可以知道,对于长字符串来说, 初始的TString.hash = G->seed
现在我们回到2.2.2中求长字符串在散列表中的位置:


在函数createstrobj中可以知道,长字符串字段TString.extra的初始值为0,那么第一次求其散列表位置需要重新更新其TString.hash值,为:

luaS_hash(str, size, ts.hash)

而长字符串的hash字段的初始值也是G->seed。

与此可以知道,不管是长字符串还是短字符串,在求其在散列表中的位置时,都是使用luaS_hash(str, size, G->seed)的值进行lmod操作后求得。

那么在两次启动lua程序过程中,字符串是没有变化的(也就是str, size都是不变的),要想遍历结果的顺序是随机的(对应散列表的位置是随机的),那么只能是说G->seed是随机的了。结果是这样子吗?

2.4 计算G->seed

直接上代码
G->seed是在函数lua_newstate()中赋值的


(1) 四个addbuff()
makeseed()函数中连用了四个addbuff,其实就是将四个变量依次copy到char buff [4*sizeof(size_t)]中。

(2) Luai_makeseed()
这个比较简单,其实就是调用C库中的time()函数,执行time(NULL)得到的是当前的时间(unix时间戳,单位为秒),
这个值在每次启动lua程序的时候,是不同的。

(3) luaS_hash(buff, p, h)
这个函数上面提到过。这里把当前Unix时间戳当做是seed,把buf字符串传入,得到一个hash值。这个值就赋给当前G->seed

所以撇开buff[]字符串的值的随机性不讲,光是unix时间戳作为h传入luaS_hash(buff, p, h)中,就知道,G->seed的值是随机的。

这样,再回到2.3.2 中计算字符串的TSTring.hash值(luaS_hash(str, size, G->seed))。

由此可以得出,先后两次执行lua程序,G->seed是随机的,那么TString.hash当然也是随机的,也就导致TString作为key时插入Table中,其对应的散列表的位置,也是随机的。

3 总结

其实篇幅这么大,主要是回顾一下table相关的东西,解释了前后两次执行lua程序,使用pairs遍历相同的table时,为什么遍历结果的顺序是随机的。

原因: 一言以蔽之,global_state.seed是一个和时间戳相关的随机值,这个值会影响字符串的TString.hash字段,当TString*作为key插入table中时,这个字段会影响其在散列表中的位置。

lua 5.3.5 使用pairs遍历table时, 遍历结果为什么是随机的相关推荐

  1. Lua中,泛型for循环遍历table时,ipairs和pairs的区别

    根据table型变量key是否为连续数字,如果是则称为数组型table,如果不是则称为非数组型table. 事实胜于雄辩,接下来通过实验来区分两组迭代器的区别. 首先给出pairs和ipairs在数组 ...

  2. html里table的遍历,jQuery遍历table

    1. $("table").find("tr").each(function(){ $(this).find("td").each(func ...

  3. lua pairs顺序遍历 table(key必须为连续数值)

    Lua常用的4中遍历方式 for key, value in pairs(tbtest) do XXX end 这样的遍历顺序并非是tbtest中table的排列顺序,而是根据tbtest中key的h ...

  4. Lua 学习笔记:C API 遍历 Table

    前情提要 Lua 通过一个虚拟栈与 C 的交互,正数索引自底向上取值,负数索引自顶向下取值. Lua 中的 Table(表)结构可以使用任何数据作为 key 进行取值.使用 C API 访问 Tabl ...

  5. golang gopher-lua 遍历table元素

    前言 gopher-lua中也有c++中的lua.next函数,不过目前我没有查到gopher-lua的next函数用法,因此这里用的是func (ls *LState) ForEach(tb *LT ...

  6. Lua的函数参数为table时奇特现象

    前言 今天在工作中使用lua编写代码时发生了一个有趣的现象,特此记录一下. 问题再现 当lua的函数为table时会发生什么情况,话不多说直接上代码: local tb = {1, 2, 3}func ...

  7. html里table的遍历,js遍历table中的tr

    js遍历table中的tr function tt(){ var table1=document.getElementById('table1'); //节点只支持getElementsByTagNa ...

  8. JavaScript遍历table

    JavaScript遍历table 1.说明      遍历表格中的某行某列,并打印其值 2.实现源码 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML ...

  9. jQuery遍历table中的tr td并获取td中的值

    jQuery遍历table中的tr td并获取td中的值 $(function(){$("#tableId tr").find("td").each(funct ...

最新文章

  1. Django在Win7下安装与创建项目hello word示例
  2. ErWin简单使用说明
  3. 相似度算法(http://blog.sina.com.cn/s/blog_62b83291010127bf.html)
  4. 小程序 国际化_在国际化您的应用程序时忘记的一件事
  5. Codeforces Round #498 (Div. 3) - 赛后补题
  6. IOS 获取系统通讯录中的联系人信息
  7. 红米k30pro工程测试代码_红米K30 PRO代号曝光,确定推出双版本,更强拍照对标荣耀30...
  8. Android开发的第一天
  9. 音乐播放器之QQ音乐最新api,亲测可用
  10. 怎么样学习Java?
  11. ArcGIS基础:合并表格(追加、合并工具)
  12. java 处理表情字符_使用轻量级工具emoji-java处理emoji表情字符
  13. 朋友圈集赞万能截图生成器威信小程序源码下载
  14. [比赛记录] 主流机器学习模型模板代码+经验分享[xgb, lgb, Keras, LR]
  15. 上海大学计算机考研改408,上海大学改考408!
  16. 2021计算机专业复试总结2
  17. 2020-10-1 交换机通过CRT保存配置-telnet
  18. 阿里云服务器新用户优惠
  19. jquery绿色版dreamweaver提示
  20. 儿童睡眠慢波的起源、同步和传播

热门文章

  1. 记一次《C语言踩内存》问题定位有感
  2. 离职后心生不满、某医院前网管“炫技性报复”,让整个医院系统瘫痪…
  3. 七款最流行的PHP本地服务器
  4. 简单介绍RESTful风格
  5. iOS App审核遇到的问题(持续更新)
  6. 用公司邮箱发错邮件怎么弄?邮件可以撤回吗?
  7. 友盟集成QQ第三方登录
  8. redis数据一致性之延时双删详解
  9. 计算机桌面出现模糊窗口,显示屏模糊,教您怎么解决电脑屏幕模糊
  10. msra数据集_ECCV 2020 | 通过聚类无标签数据来提高人脸识别能力