lua的table表处理 及注意事项

   2023-02-09 学习力0
核心提示:lua,一款很轻量级很nice很强大的脚本语言,做为lua中使用最为频繁的table表,在使用之时还是有颇多的好处与坑的;下面是大牛 云风的一片关于lua table的blog,可使得对lua table内在机制 窥测一二;lua 的整体效率是很高的,其中,它的 table 实现的很巧妙为

lua,一款很轻量级很nice很强大的脚本语言,做为lua中使用最为频繁的table表,在使用之时还是有颇多的好处与坑的;

下面是大牛 云风的一片关于lua table的blog,可使得对lua table内在机制 窥测一二;

lua 的整体效率是很高的,其中,它的 table 实现的很巧妙为这个效率贡献很大。

lua 的 table 充当了数组和映射表的双重功能,所以在实现时就考虑了这些,让 table 在做数组使用时尽量少效率惩罚。

lua 是这样做的。它把一个 table 分成数组段和 hash 段两个部分。数字 key 一般放在数组段中,没有初始化过的 key 值全部设置为 nil 。当数字 key 过于离散的时候,部分较大的数字 key 会被移到 hash段中去。这个分割线是以数组段的利用率不低于 50% 为准。 0 和 负数做 key 时是肯定放在 hash 段中的。

string 和 number 都放在一起做 hash ,分别有各自的算法,但是 hash 的结果都在一个数值段中。hash 段采用闭散列方法,即,所有的值都存在于表中。如果hash 发生碰撞,额外的数据记在空闲槽位里,而不额外分配空间存放。当整个个表放满后,hash 段会扩大,所有段内的数据将被重新 hash ,重新 hash 后,冲突将大大减少。

这种 table 的实现策略,首先保证的是查找效率。对于把 table 当数组使用时将和 C 数组一样高效。对于 hash 段的值,查找几乎就是计算 hash 值的过程(其中string 的 hash 值是事先计算好保存的),只有在碰撞的时候才会有少许的额外查找时间,而空间也不至于过于浪费。在 hash 表比较满时,插入较容易发生碰撞,这个时候,则需要在表中找到空的插槽。lua 在table 的结构中记录了一个指针顺次从一头向另一头循序插入来解决空槽的检索。每个槽点在记录 next 指针保存被碰撞的 key 的关联性。

整个来说,这种解决方法是非常不错的。

关于映射表的实现,我前段时间也做过一个别的研究。贴在留言本上:
<a href="http://www.codingnow.com/2004/board/view.php?paster=777&reply=0">树表结合的一种映射表实现</a>
<a href="http://www.codingnow.com/2004/board/view.php?paster=776&reply=0">在 vector , map , list 间取得平衡</a>

原文链接: http://blog.codingnow.com/2005/10/lua_table.html

 

即便作为lua 开发蛮久的coder来讲,很多东西不亲自去考究一下,还不是很清晰的,不如lua table的长度问题就是一个 很奇葩的例子;下面的这些也许你就不是很清楚了;

想要取得lua table长度,有这么几种方法,table.getn(table_name), #table_name, table.maxn(table_name), 再加上 ipairs(table_name) 和pairs(table_name)遍历;

根据云风所写的文章可以得知,lua的两种不同的存储方式,自然的,上述的几种 取得lua table 长度的几种方式也 存在区别;

table.maxn(table)

table.maxn()函数返回指定table中所有正数key值中最大的key值. 如果不存在key值为正数的元素, 则返回0. 此函数不限于table的数组部分.

table.getn(table)
返回table中元素的个数

#(table)

返回的是lua table 中key为连续整型数字(抑或是 默认整型)的长度数;

pairs()函数基本和ipairs()函数用法相同, 区别在于pairs()可以遍历整个table, 即包括数组及非数组部分.

关于对lua table长度的问题,http://blog.csdn.net/dssdss123/article/details/12676329 对于一些奇葩问题的讲述还是有些深入的;亲测【lua version=5.1.4】,的确如此;

但是还是有些东西需要补充的,对于 #用法,其表现和table.getn()在很多极端的情况下都是类似的;table.maxn(),因为获取的是table中所有正数key值中最大的key值.可以不连续;

local tblTest =
{
    "this 1",
    "this 2",
    [3] = 2,
    [4] = 5,
    [5] = 7,
    "this 3",
    [10] = 10,
}
print(table.getn(tblTest))
print(#(tblTest))
print(table.maxn(tblTest))

--===========================
>lua -e "io.stdout:setvbuf 'no'" "filedeal.lua" 
5
5
10
--===========================

local tblTest =
{
    "this 1",
    [3] = 2,
    [4] = 5,
    [5] = 7,
    "this 3",
    --"adsfasd",
    [10] = 10,
}
print(table.getn(tblTest))
print(#(tblTest))
print(table.maxn(tblTest))
--===========================
>lua -e "io.stdout:setvbuf 'no'" "filedeal.lua" 
5
5
10
--===========================


local tblTest =
{
    "this 1",
    [3] = 2,
    [4] = 5,
    [5] = 7,
    --"adsfasd",
    [10] = 10,
}
print(table.getn(tblTest))
print(#(tblTest))
print(table.maxn(tblTest))
--===========================
>lua -e "io.stdout:setvbuf 'no'" "filedeal.lua" 
1
1
10
--===========================
    

上述三个tabTest 的不同在于 这都是lua默认的下表是从1开始,有两个的默认的,使得 [3]=2,这项元素足以将连续性接上,当接不上的时候,因为不连续行,自然打印值有所不同了;

倘若在后面或者该表 其中再坠入一个nil,所输出来的内容又是不一样的:所以~不要在lua的table中使用nil值,如果一个元素要删除,直接remove,不要用nil去代替。

倘若再将元素后面加一项 下表默认的元素,其结果会怎样呢? 例 如下代码:

local tblTest =
{
    "this 1",
    [3] = 2,
    [4] = 5,
    [5] = 7,
    "this 3",
    --"adsfasd",
    [10] = 10,
}
for k , v in ipairs(tblTest) do
    print(k,v)
end
--=============================
1    this 1
2    this 3
3    2
4    5
5    7
--=============================

local tblTest =
{
    "this 1",
    [3] = 2,
    [4] = 5,
    [5] = 7,
    "this 3",
    "this 4",
    --"adsfasd",
    [10] = 10,
}
for k , v in ipairs(tblTest) do
    print(k,v)
end

--=============================
1    this 1
2    this 3
3    this 4
4    5
5    7
--=============================

由此可见,lua 默认的下表值会将显示的覆盖,即便是 再调整下顺序 也是如是,至于为何如此,不理解,有待参悟【欢求 大神指正】;

这样的意外在lua中 挺多的,只要明白了 基本的原理,倒也不足为奇了;

至于lua table的遍历 可参见 http://rangercyh.blog.51cto.com/1444712/1032925 这这篇文章;讲的挺详细的;

另外: 关于lua table的其他小问题:

1, 配置lua table 元素之间,以”,“ 或者”;“完全是一样的【可参见lua手册】,看你的爱好了,推荐的是:用分好可以作为 元素类型的不同而分割开显示下;

2,不要在lua的table中使用nil值,如果一个元素要删除,直接remove,不要用nil去代替。

3,判断lua table 是否为nil 不能用 if a == {} then 【错误的】(这样的结果就是a == {}永远返回false,是一个逻辑错误。因为这里比较的是table a和一个匿名table的内存地址。);

   if table.maxn(a) == 0 then 【错误的】这样做不保险啊,除非table的key都是数字,而没有hash部分。

  if #(a)  == 0 then 也是不靠谱的,除非你能保证没人这样写这个table like this:tab = {nil,1,} 用#tab print出来 的确是0, 能说此tab是nil的?

   可以使用lua内置的next来判断; if next(a) == 0 then ;

4,应该尽量使用 local 变量而非 global 变量。这是 Lua 初学者最容易犯的错误。 global 变量实际上是放在一张全局的 table 里的。 global 变量实际上是利用一个 string (变量名作 key) 去访问这个 table 。 虽然[InterWiki]Lua5 的 table 效率很高 ,但是相对于 local 变量,依然有很大的效率损失。 local 变量是直接通过 Lua 的堆栈访问的。有些 global 变量的访问是不经意的,比如双重for中使用的pairs或者ipirs(全局函数),如果在使用循环外层 local pairs=pairs会对性能有些不同层次的提升;

5, 警惕临时变量 字符串的连接操作,会产生新的对象。这是由 lua 本身的 string 管理机制导致的。lua 在 VM 内对相同的 string 永远只保留一份唯一 copy ,这样,所有字符串比较就可以简化为地址比较。这也是 lua 的 table 工作很快的原因之一。这种 string 管理的策略,跟 java 等一样,所以跟 java 一样,应该尽量避免在循环内不断的连接字符串,比如 a = a..x 这样。每次运行,都很可能会生成一份新的 copy 。

6, 同样,记住,每次构造一份 table 都会多一份 table 的 copy 。比如在 lua 里,把平面坐标封装成 { x, y } 用于参数传递,就需要考虑这个问题。每次你想构造一个坐标对象传递给一个函数,{ 10,20 } 这样明确的写出,都会构造一个新的 table 出来。要么,我们想办法考虑 table 的重用;要么,干脆用 x,y 两个参数传递坐标。 同样需要注意的是以 function foo (...) 这种方式定义函数, ... 这种不定参数,每次调用的时候都会被定义出一个 table 存放不定数量的参数。 这些临时构造的对象往往要到 gc 的时候才被回收,过于频繁的 gc 有时候正是效率瓶颈。

7,【未完待续...】

 

 
反对 0举报 0 评论 0
 

免责声明:本文仅代表作者个人观点,与乐学笔记(本网)无关。其原创性以及文中陈述文字和内容未经本站证实,对本文以及其中全部或者部分内容、文字的真实性、完整性、及时性本站不作任何保证或承诺,请读者仅作参考,并请自行核实相关内容。
    本网站有部分内容均转载自其它媒体,转载目的在于传递更多信息,并不代表本网赞同其观点和对其真实性负责,若因作品内容、知识产权、版权和其他问题,请及时提供相关证明等材料并与我们留言联系,本网站将在规定时间内给予删除等相关处理.

  • LUA解析json小demo
    需要修改的json数据gui-config.json{"configs": [{"server": "JP3.ISS.TF","server_port": 443,"password": "58603228","method": "aes-256-cfb","remarks": ""},{"serv
    03-16
  • 第二十三篇:在SOUI中使用LUA脚本开发界面
    像写网页一样做客户端界面可能是很多客户端开发的理想。做好一个可以实现和用户交互的动态网页应该包含两个部分:使用html做网页的布局,使用脚本如vbscript,javascript做用户交互的逻辑。当需求变化时,只需要在服务端把相关代码调整一下,用户即可看到新的
    03-16
  • windows下编译lua源码"><转>windows下编译lua源
    因为之前一直使用 lua for windows 来搭建lua的使用环境,但是最新的 lua for windows 还没有lua5.2,我又想用这个版本的lua,所以被逼无奈只能自己编一下lua源码。首先从 lua的官网 下载你想要使用的lua源码,比如我下载的就是lua5.2。解压后内容如下:
    03-16
  • lua:使用Lua处理游戏数据
    在之前lua学习:lua作配置文件里,我们学会了用lua作配置文件。其实lua在游戏开发中可以作为一个强大的保存、载入游戏数据的工具。 比如说,现在我有一份表单:data.xls用什么工具解析这个Excel文件并将数据载入游戏?我们可以使用Lua来完成这个工作。不过要
    03-16
  • 第1课 - 学习 Lua 的意义
    第1课 - 学习 Lua 的意义
    第1课 - 学习 Lua 的意义1.Lua 简介           (1) 1993年、巴西(2) 小巧精致的脚本语言,大小只有 200K(3) 用标准C语言写成,能够在所有的平台上编译运行(4) 发明的目标是嵌入在C/C++中,为应用程序提供灵活的扩展和定制功能(5) 不适合用于开发
    03-16
  • RedisTemplate 常用API+事务+陷阱+序列化+pipeline+LUA
    RedisTemplate 常用API+事务+陷阱+序列化+pipel
    https://www.jianshu.com/p/7bf5dc61ca06/https://blog.csdn.net/qq_34021712/article/details/79606551https://www.jianshu.com/p/c9f5718e58f0dependencygroupIdorg.springframework.boot/groupIdartifactIdspring-boot-starter-data-redis/artifactId/depe
    03-08
  • Nginx动态路由的新姿势:使用Go取代lua nginx路由规则
    Nginx动态路由的新姿势:使用Go取代lua nginx路
    导语: 在Nitro 中, 我们需要一款专业的负载均衡器。 经过一番研究之后,Mihai Todor和我使用Go构建了基于Nginx、Redis 协议的路由器解决方案,其中nginx负责所有繁重工作,路由器本身并不承载流量。 这个解决方案过去一年在生产环境中运行顺畅。 以下是我
    03-08
  • cocos2d-lua 控制台输入Lua指令方便调试
    用脚本进行开发,如果不能实时去输入指令,就丧失了脚本的一大特色,所以对cocos2d-x程序稍微修改下,使其可以直接从控制台读入lua指令,方便调试。1 首先在行首加入lua的引用,如下1 #include "main.h"2 #include "AppDelegate.h"3 #include "cocos2d.h"4 #i
    02-09
  • lua_touserdata
    void *lua_touserdata(lua_State*L,intindex);如果给定索引处的值是一个完整的userdata,函数返回内存块的地址。如果值是一个lightuserdata,那么就返回它表示的指针。否则,返回NULL。例如: 在CCLuaStack::executeFunction()函数中有一段代码是用来获取c++
    02-09
  • Lua 5.2 中文参考手册
    闲来无事,发现Lua更新到了5.2.2,参考手册也更到了5.2,在网上发现只有云风翻译的5.1版,花了几天时间翻译了一些。参考手册有点长,又要随时修改,所以在github上建了项目,有需要的朋友可以看看,同时也欢迎指正。中文手册:Lua 5.2中文参考手册
    02-09
点击排行