luatable,luatable 预填充

http://www.itjxue.com  2023-01-23 16:56  来源:未知  点击次数: 

由浅入深的理解Lua的数据结构——table

思考一下:如果现在定义了一个table a,将table a赋值给table b,此时它们的内存情况是什么样呢?

ab都会指向同一个内存块,如果a设置为nil,b依旧能访问该内存块的元素,直到b设置为nil后,Lua的垃圾回收机制会清理相应的内存。所以当b在更改table内的值后,a再去访问的时候,值也是改变的。

table的for循环写法

先解释一下上面的各个变量

在table中,我们无法对两个table之间进行操作,比如:table a+ table b。为了解决这个问题,就引入了元表的概念,它允许我们 改变table的行为 。

当我们在进行表a+b的时候:

其实逼逼了这么多,我觉得元表和元方法其实就相当于 重载 ,比如_add,我们就是重载了+操作符

也可以将它理解成 事件驱动 ,元表中的键对应着不同的事件名,关联的值是元方法,元方法里就是我们事件对应的操作。

继续接上面的分析

每个Node都是一个键值对 ,里面包含了key和value。tvk是key的值,但是当我们出现hash冲突,此时lua的hash算法比较特殊,一般情况下,我们的hash算法都是根据key算出hash,然后如果有冲突的话,就放在改位置的链表上。而lua不同, 当它出现hash冲突的时候,会在hash表中找到一个空的位置x,来存放key,并且让冲突处的节点的nk.next指向x 。

这意味着什么呢?发生冲突我们无需重新分配空间来存储冲突的key,而是利用hash表上未用过的空白处来存储。刚才我们将key放在了空位置x, 如果此时存在另一个key2,它算出的hash值是空位置x,而我们刚才的key只是因为hash冲突了,占用了其他key的位置,这个时候我们就讲key2放在x上,将key再放到另一个空白处 。

忍不住想总结一波table的实现,和上面我们的Node类型进行对照

lua的table其实由 数组段 和 hash 两部分组成,当你的key值不会过于离散的时候,lua就会将它存储在数组段(也就是下图的array),反正会存储在hash段(也就是下图的node),这个分割线是以数组段的利用率不低于50%为准。hash段采用闭散列的算法,它将有冲突的key存储在空闲槽中,而不额外分配内存。

在我们table结构体中,array和sizearray都是表示数组段。

而lsizenode和node,lastfree是表示hash段。node指向hash表的起始位置,lsizenode是log2(node指向的哈希表的节点数目), lastfree指向node里面最后一个未使用的节点 (因为我们在hash冲突的时候,是从后往前查找未使用的节点,lastfree存储最后一个未使用节点就可以方便查找)

如果此时hash表中已经没有空格了,那么lua就会resize这个hash表(等会再谈lua的动态扩增)

lua创建新表的时候 先为新表分配内存 Table * t = luaM_new(L, Table),然后将表 连接到gc 上并设置标志位luaC_link(L, obj2gco(t), LUA_TTABLE),然后 初始化 一些必要的属性,使用setarrayvector为数组段分配内存,setnodevector为hash部分分配内存,最后返回表指针。

table的查找会根据key进行判断,如果key为空就直接返回空,key为字符串就调用luaH_getstr(t, rawtsvalue(key)),key为数字则根据它是否整数调用luaH_getnum(t, k),否则,计算出key的位置,遍历table的node节点,找到对应键所在的节点返回。

因为key为数字比较特殊,所以研究一把luaH_getnum函数的实现

如果key大于等于1,小于数组的长度,则从数组中取出对应的键值,否则利用hashnum找到key对应的node位置,遍历node链表,返回对应的值

dummynode是带头结点的指针。

往table中插入新值,先检测 key的主位置 (main position)是否为空,主位置就是key的哈希值在node中的位置。

如果主位置为空,就直接插入,主位置不为空,检查占领该位置的key的主位置是不是在这个地方,如果不在,则将该key移动到其他空闲位置,将要插入的key插入到这个位置中。如果在这个地方,则将要插入的key插入到一个 空槽 中。

如果找不到空闲位置放新键值,就 rehash函数 ,扩增hash表的大小,再找出新位置,再调用luaH_set把要插入的key插入到新的哈希表中,直接返回LuaH_set的结果。

lua 怎么获取table长度?

1、table.getn(tableName) 得到一个table的大小,等同于操作符# ?

要注意的是:该table的key必须是有序的,索引是从1开始的。

2、例如有序的

local xiang = {10,22,34,42,51} ?

print("xiang length ==",table.getn(xiang)) --结果为:[LUA-print] xiang length == ? ?5

3、例如无序的

local song = {s=10,h=22,x=34,m=42,n=51} ?

print("song length ==",table.getn(song)) --结果为:[LUA-print] song length == ? ?0

4、对于无序的我们可以这样做

local count = 0 ?

for k,v in pairs(song) do

count = count + 1 ?

end ?

print("song length ==",count) --结果为: [LUA-print] song length == 5

1、Lua

Lua脚本可以很容易的被C/C++?代码调用,也可以反过来调用C/C++的函数,这使得Lua在应用程序中可以被广泛应用。不仅仅作为扩展脚本,也可以作为普通的配置文件,代替XML,ini等文件格式,并且更容易理解和维护。

2、保存和运行

运行可以通过 Lua 的交互模式,也可以用记事本编辑代码保存为 .lua 的格式,通过 lua 编译器运行。也可以通过第三方工具,将 lua 打包独立运行。

Lua中table的赋值、作为函数参数传递和克隆的问题

lua中table的赋值,其实传递的是地址,操作被赋值的表内的 元素 ,会影响原来的table;但如果操作的是被赋值的表这个变量本身,相当于改变了其“指针的指向”,不会影响到原来的table了。

弄懂了table赋值的问题,它作为函数参数的问题就迎刃而解了。函数内部使用的参数table,其实就是使用的它的拷贝(赋值)。

table的赋值会使两个表产生关联,可能导致一些意外的修改。为了解决这个问题,就需要用到table的克隆。

luaci给的方法:

Lua遍历table

Lua中遍历table主要有四种方式,各有各的不同

Lua的存储并不是顺序存储的,所以当我们使用第一种遍历方法去遍历的时候,输出的值可能和我们想象的顺序不同。

在我们的设想里,我们是希望它输出1,2,3,4,但是实际上输出的是1,2,4,3。for k,v in ipairs(tbtest) do是根据key中的hash值的排列顺序来排列的。

它的前提是key是从1开始的,那么它会按照key的大小顺序进行遍历,如果key不连续,它就不会接下去遍历

上面的函数只会打印1,2,3不会打印5

这样就一个都不会遍历

因为#table 是获取table里的数组段的长度,遍历时只会输出数组段的值,并且它的前提也是key必须从1开始,如果key不从1开始,#tbtest获取到的值就是0

它获取的是数组段的key的最大值,字符串的key是无法获取到的

其实maxn还是很有用处的,用它取到最大的key值,我们就可以从前往后遍历数据了,完全可以略过nil

while循环与其他语言循环几乎相同。

repeat..until在lua5.1之后和其他语言有一些细微的不同,就是在循环体的局部变量的作用域扩增到条件测试中,也就是until语句。

(责任编辑:IT教学网)

更多

相关服务器空间文章

推荐服务器空间文章