浅析Lua中table的遍历

使用luajava实现lua遍历文件 首先需要说明luaj和luajava是一个东西 luaj是java的一个库 而luajava是lua的库 luaj允许java运行lua代码 也可以调用lua的变量或全局库ENV等等 而luajava是允许lua调用java对象 其中包括但限于构造方法 成员方法 静态方法等 但调用的这些方法必须是公有的 也就是public修饰的方法那么该怎么调用他呢?如果你使用的是gg的话 那比较简单 首先你要先找到支持luajava的gg 有rlgg elgg 还有独猫的luajava库的gg。 阅读详情

当我在工作中使用lua进行开发时,发现在lua中有4种方式遍历一个table,当然,从本质上来说其实都一样,只是形式不同,这四种方式分别是:


  • for
     key, value in pairs(tbtest) do  
  • XXX  
  • end 
  •  
  • for key, value in ipairs(tbtest) do  
  • XXX  
  • end 
  •  
  • for i=1, #(tbtest) do  
  •     XXX  
  • end 
  •  
  • for i=1, table.maxn(tbtest) do  
  •     XXX  
  • end

     

    前两种是泛型遍历,后两种是数值型遍历。当然你还会说lua的table遍历还有很多种方法啊,没错,不过最常见的这些遍历确实有必要弄清楚。

    这四种方式各有特点,由于在工作中我几乎每天都会使用遍历table的方法,一开始也非常困惑这些方式的不同,一段时间后才渐渐明白,这里我也是把自己的一点经验告诉大家,对跟我一样的lua初学者也许有些帮助(至少当初我在写的时候在网上就找了很久,不知道是因为大牛们都认为这些很简单,不需要说,还是因为我笨,连这都要问)。

    首先要明确一点,就是lua中table并非像是C/C++中的数组一样是顺序存储的,准确来说lua中的table更加像是C++中的map,通过Key对应存储Value,但是并非顺序来保存key-value对,而是使用了hash的方式,这样能够更加快速的访问key对应的value,我们也知道hash表的遍历需要使用所谓的迭代器来进行,同样,lua也有自己的迭代器,就是上面4种遍历方式中的pairs和ipairs遍历。但是lua同时提供了按照key来遍历的方式(另外两种,实质上是一种),正式因为它提供了这种按key的遍历,才造成了我一开始的困惑,我一度认为lua中关于table的遍历是按照我table定义key的顺序来的。

    下面依次来讲讲四种遍历方式,首先来看for k,v in pairs(tbtest) do这种方式:

    先看效果:

    1. tbtest = {  
    2.     [1] = 1,  
    3.     [2] = 2,  
    4.     [3] = 3,  
    5.     [4] = 4,  
    6.  
    7. for key, value in pairs(tbtest) do  
    8.     print(value)  
    9. end 

    我认为输出应该是1,2,3,4,实际上的输出是1,2,4,3。我因为这个造成了一个bug,这是后话。

    也就是说for k,v in pairs(tbtest) do 这样的遍历顺序并非是tbtest中table的排列顺序,而是根据tbtest中key的hash值排列的顺序来遍历的。

     

    当然,同时lua也提供了按照key的大小顺序来遍历的,注意,是大小顺序,仍然不是key定义的顺序,这种遍历方式就是for k,v in ipairs(tbtest) do。

    for k,v in ipairs(tbtest) do 这样的循环必须要求tbtest中的key为顺序的,而且必须是从1开始,ipairs只会从1开始按连续的key顺序遍历到key不连续为止。

    1. tbtest = {  
    2. [1] = 1,  
    3. [2] = 2,  
    4. [3] = 3,  
    5. [5] = 5,  
    6.  
    7. for k,v in ipairs(tbtest) do  
    8. print(v)  
    9. end 

    只会打印1,2,3。而5则不会显示。

    1. local tbtest = {  
    2. [2] = 2,  
    3. [3] = 3,  
    4. [5] = 5,  
    5.  
    6. for k,v in ipairs(tbtest) do  
    7. print(v)  
    8. end 

    这样就一个都不会打印。

     

    第三种遍历方式有一种神奇的符号'#',这个符号的作用是是获取table的长度,比如:

    1. tbtest = {  
    2. [1] = 1,  
    3. [2] = 2,  
    4. [3] = 3,  
    5. }  
    6. print(#(tbtest)) 

    打印的就是3

    1. tbtest = {  
    2. [1] = 1,  
    3. [2] = 2,  
    4. [6] = 6,  
    5. }  
    6. print(#(tbtest)) 

    这样打印的就是2,而且和table内的定义顺序没有关系,无论你是否先定义的key为6的值,‘#’都会查找key为1的值开始。

    如果table的定义是这样的:

    1. tbtest = {  
    2. ["a"] = 1,  
    3. [2] = 2,  
    4. [3] = 3,  
    5.  
    6. print(#(tbtest)) 

    那么打印的就是0了。因为‘#’没有找到key为1的值。同样:

    1. tbtest = {  
    2. [“a”] = 1,  
    3. [“b”] = 2,  
    4. [“c”] = 3,  
    5. }  
    6. print(#(tbtest)) 

    打印的也是0

    所以,for i=1, #(tbtest) do这种遍历,只能遍历当tbtest中存在key为1的value时才会出现结果,而且是按照key从1开始依次递增1的顺序来遍历,找到一个递增不是1的时候就结束不再遍历,无论后面是否仍然是顺序的key,比如:

     

    table.maxn获取的只针对整数的key,字符串的key是没办法获取到的,比如:

    1. tbtest = {  
    2. [1] = 1,  
    3. [2] = 2,  
    4. [3] = 3,  
    5. }  
    6. print(table.maxn(tbtest)) 
    7.  
    8.  
    9. tbtest = {  
    10. [6] = 6,  
    11. [1] = 1,  
    12. [2] = 2,  
    13. }  
    14. print(table.maxn(tbtest)) 

    这样打印的就是3和6,而且和table内的定义顺序没有关系,无论你是否先定义的key为6的值,table.maxn都会获取整数型key中的最大值。

    如果table的定义是这样的:

    1. tbtest = {  
    2. ["a"] = 1,  
    3. [2] = 2,  
    4. [3] = 3,  
    5. }  
    6. print(table.maxn(tbtest)) 

    那么打印的就是3了。如果table是:

    1. tbtest = {  
    2. [“a”] = 1,  
    3. [“b”] = 2,  
    4. [“c”] = 3,  
    5. }  
    6. print(table.maxn(tbtest))  
    7. print(#(tbtest)) 

    那么打印的就全部是0了。

     

     

    换句话说,事实上因为lua中table的构造表达式非常灵活,在同一个table中,你可以随意定义各种你想要的内容,比如:

    1. tbtest = {  
    2. [1] = 1,  
    3. [2] = 2,  
    4. [3] = 3,  
    5. ["a"] = 4,  
    6. ["b"] = 5,  

    同时由于这个灵活性,你也没有办法获取整个table的长度,其实在coding的过程中,你会发现,你真正想要获取整个table长度的地方几乎没有,你总能采取一种非常巧妙的定义方式,把这种需要获取整个table长度的操作避免掉,比如:

    1. tbtest = {  
    2. tbaaa = {  
    3. [1] = 1,  
    4. [2] = 2,  
    5. [3] = 3,  
    6. },  
    7. ["a"] = 4,  
    8. ["b"] = 5,  

    你可能会惊讶,上面这种table该如何遍历呢?

    1. for k, v in pairs(tbtest) do  
    2. print(k, v)  
    3. end 

    输出是:a 4 b 5 tbaaa table:XXXXX。

    由此你可以看到,其实在table中定义一个table,这个table的名字就是key,对应的内容其实是table的地址。

    当然,如果你用

    1. for k, v in ipairs(tbtest) do  
    2. print(k,v)  
    3. end 

    来遍历的话,就什么都不会打印,因为没有key为1的值。但当你增加一个key为1的值时,ipairs只会打印那一个值,现在你明白ipairs是如何工作的吧。

    既然这里谈到了遍历,就说一下目前看到的几种针对table的遍历方式:

    for i=1, #tbtest do --这种方式无法遍历所有的元素,因为'#'只会获取tbtest中从key为1开始的key连续的那几个元素,如果没有key为1,那么这个循环将无法进入

    for i=1, table.maxn(tbtest) do --这种方式同样无法遍历所有的元素,因为table.maxn只会获取key为整数中最大的那个数,遍历的元素其实是查找tbtest[1]~tbtest[整数key中最大值],所以,对于string做key的元素不会去查找,而且这么查找的效率低下,因为如果你整数key中定义的最大的key是10000,然而10000以下的key没有几个,那么这么遍历会浪费很多时间,因为会从1开始直到10000每一个元素都会查找一遍,实际上大多数元素都是不存在的,比如:

    1. tbtest = {  
    2. [1] = 1,  
    3. [10000] = 2,  
    4. }  
    5. local count = 0  
    6. for i=1, table.maxn(tbtest) do  
    7. count = count + 1  
    8. print(tbtest[i])  
    9. end  
    10. print(count

    你会看到打印结果是多么的坑爹,只有1和10000是有意义的,其他的全是nil,而且count是10000。耗时非常久。一般我不这么遍历。但是有一种情况下又必须这么遍历,这个在我的工作中还真的遇到了,这是后话,等讲完了再谈。

    1. for k, v in pairs(tbtest) do 

    这个是唯一一种可以保证遍历tbtest中每一个元素的方式,别高兴的太早,这种遍历也有它自身的缺点,就是遍历的顺序不是按照tbtest定义的顺序来遍历的,这个前面讲到过,当然,对于不需要顺序遍历的用法,这个是唯一可靠的遍历方式。

    1. for k, v in ipairs(tbtest) do 

    这个只会遍历tbtest中key为整数,而且必须从1开始的那些连续元素,如果没有1开始的key,那么这个遍历是无效的,我个人认为这种遍历方式完全可以被改造table和for i=1, #(tbtest) do的方式来代替,因为ipairs的效果和'#'的效果,在遍历的时候是类似的,都是按照key的递增1顺序来遍历。

    好,再来谈谈为什么我需要使用table.maxn这种非常浪费的方式来遍历,在工作中, 我遇到一个问题,就是需要把当前的周序,转换成对应的奖励,简单来说,就是从一个活动开始算起,每周的奖励都不是固定的,比如1~4周给一种奖励,5~8周给另一种奖励,或者是一种排名奖励,1~8名给一种奖励,9~16名给另一种奖励,这种情况下,我根据长久的C语言的习惯,会把table定义成这个样子:

    1. tbtestAward = {  
    2. [8] = 1,  
    3. [16] = 3,  

    这个代表,1~8给奖励1,9~16给奖励3。这样定义的好处是奖励我只需要写一次(这里的奖励用数字做了简化,实际上奖励也是一个大的table,里面还有非常复杂的结构)。然后我就遇到一个问题,即我需要根据周序数,或者是排名序数来确定给哪一种奖励,比如当前周序数是5,那么我应该给我定义好的key为8的那一档奖励,或者当前周序数是15,那么我应该给奖励3。由此读者看出,其实我定义的key是一个分界,小于这个key而大于上一个key,那么就给这个key的奖励,这就是我判断的条件。逻辑上没有问题,但是lua的遍历方式却把我狠狠地坑了一把。读者可以自己想一想我上面介绍的4种遍历方式,该用哪一种来实现我的这种需求呢?这个函数的大致框架如下:

    1. function GetAward(nSeq)  
    2. for 遍历整个奖励表 do  
    3. if 满足key的条件 then  
    4. return 返回对应奖励的key  
    5. end  
    6. end  
    7. return nil  
    8. end 

    我也不卖关子了,分别来说一说吧,首先因为我的key不是连续的,而且没有key为1的值,所以ipairs和'#'遍历是没用的。这种情况下理想的遍历貌似是pairs,因为它会遍历我的每一个元素,但是读者不要忘记了,pairs遍历并非是按照我定义的顺序来遍历,如果我真的使用的条件是:序数nSeq小于这个key而大于上一个key,那么就返回这个key。那么我无法保证程序执行的正确性,因为key的顺序有可能是乱的,也就是有可能先遍历到的是key为16的值,然后才是key为8的值。

    这么看来我只剩下table.maxn这么一种方式了,于是我写下了这种代码:

    1. for i=1, table.maxn(tbtestAward) do  
    2. if tbtestAward[i] ~= nil then  
    3. if nSeq <= i then  
    4. return i  
    5. end  
    6. end  
    7. end  

    这么写效率确实低下,因为实际上还是遍历了从key为1开始直到key为table.maxn中间的每一个值,不过能够满足我上面的要求。当时我是这么实现的,因为这个奖励表会不断的发生变化,这样我每次修改只需要修改这个奖励表就能够满足要求了,后来我想了想,觉得其实我如果自己再定义一个序数转换成对应的奖励数种类的表就可以避免这种坑爹的操作了,不过如果奖励发生修改,我需要统一排查的地方就不止这个奖励表了,权衡再三,我还是没有改,就这么写了。没办法,不断变化的需求已经把我磨练的忘记了程序的最高理想。我甚至愿意牺牲算法的效率而去追求改动的稳定性。在此哀悼程序员的无奈。我这种时间换空间的做法确实不知道好不好。

    后来我在《Programming In Lua》中看到了一个神奇的迭代器,使用它就可以达到我想要的这种遍历方式,而且不需要去遍历那些不存在的key。它的方法是把你所需要遍历的table里的key按照遍历顺序放到另一个临时的table中去,这样只需要遍历这个临时的table按顺序取出原table中的key就可以了。如下:

    首先定义一个迭代器:

    1. function pairsByKeys(t)  
    2.     local a = {}  
    3.     for n in pairs(t) do  
    4.         a[#a+1] = n  
    5.     end  
    6.     table.sort(a)  
    7.     local i = 0  
    8.     return function()  
    9.         i = i + 1  
    10.         return a[i], t[a[i]]  
    11.     end  
    12. end 

    然后在遍历的时候使用这个迭代器就可以了,table同上,遍历如下:

    1. for key, value in pairsByKeys(tbtestAward) do  
    2.   if nSeq <= key then  
    3. return key  
    4. end  
    5. end 

    并且后来我发现有了这个迭代器,我根本不需要先做一步获取是哪一档次的奖励的操作,直接使用这个迭代器进行发奖就可以了。大师就是大师,我怎么就没想到呢!

    还有些话我还没有说,比如上面数值型遍历也并非是像看起来那样进行遍历的,比如下面的遍历:

    1. tbtest = {  
    2.     [1] = 1,  
    3.     [2] = 2,  
    4.     [3] = 3,  
    5.     [5] = 5,  
    6.  
    7. for i=1, #(tbtest) do  
    8.     print(tbtest[i])  
    9. end 

    打印的顺序是:1,2,3。不会打印5,因为5已经不在table的数组数据块中了,我估计是被放到了hash数据块中,但是当我修改其中的一些key时,比如:

    1. tbtest = {  
    2.     [1] = 1,  
    3.     [2] = 2,  
    4.     [4] = 4,  
    5.     [5] = 5,  
    6.  
    7. for i=1, #(tbtest) do  
    8.     print(tbtest[i])  
    9. end 

    打印的内容却是:1,2,nil,4,5。这个地方又遍历到了中间没有的key值,并且还能继续遍历下去。我最近正在看lua源码中table的实现部分,已经明白了是怎么回事,不过我想等我能够更加清晰的阐述lua中table的实现过程了再向大家介绍。用我师傅的话说就是不要使用一些未定义的行为方法,避免在工作中出错,不过工作外,我还是希望能明白未定义的行为中那些必然性,o(︶︿︶)o 唉!因果论的孩子伤不起。等我下一篇博文分析lua源码中table的实现就能够更加清晰的说明这些了。

【无标题】IP804A参考资料和管脚作用图 3.3V内置led控制内置EEPROM接口,用于哑应用ip804a,可集成为4X N端口PSE稳压器内置支持外部设备。多个系统和IC总线用于从每个IP804A收集PD电源状态,以支持全局电源管理。多个ip804a可以构建一个具有成本效益的PHY级PSE系统以支持PD分类和电源管理,而无需主机。基于LLDP(IEEE标准8023AT-2009的一部分),可以实时维护PSE和PD之间的动态电源管理,以提高电源效率。IP804A是一款用于POE(以太网供电)系统的4端口PSE(电源设备)控制器IC。 阅读详情

相关推荐

Lua基础 第2章】lua遍历table的方式、运算符、math库、字符串操作方法

文章对你有所帮助的话,欢迎给个赞或者 star,你的支持是对作者最大的鼓励,足之处可以在评论区多多指正,交流学习。

KJ.JK 8480

Luatable遍历详解

主要介绍了Luatable遍历详解,本文讲解了4种遍历方法,并详细分析了这4种方法的特点、使用环境、相关知识等,需要的朋友可以参考下

JavaSwing_1.1: FlowLayout(流式布局)

FlowLayout,流式布局管理器。按水平方向依次排列放置组件,排满一行,换下一行继续排列。

谢TS的博客 7万+

lua遍历table的几种方式

lua遍历table的几种方式 lua中的table是一张hash表,遍历table是一个非常常见的操作,但是由于table中的内容同,其遍历方式也尽相同;而且若使用了错误的遍历方式,还会导致意外中断遍历,让结果符合预期。 本文主要是简单介绍一下lua table的几种遍历方式 总览 lua常用的遍历方式有三种,使用ipairs遍历、使用pairs遍历、使用i=1,#xxx遍历...

绯浅yousa的笔记 5万+

lua 遍历删除_浅析Luatable遍历和删除

当我在工作中使用lua进行开发时,发现在lua中有4种方式遍历一个table,当然,从本质上来说其实都一样,只是形式同,这四种方式分别是:forkey,valueinpairs(tbtest)doXXXendforkey,valueinipairs(tbtest)doXXXendfori=1,#(tbtest)doXXXendfori=1,table.maxn(tbtest)doX...

weixin_29243279的博客 1378

【转】浅析Luatable遍历

当我在工作中使用lua进行开发时,发现在lua中有4种方式遍历一个table,当然,从本质上来说其实都一样,只是形式同,这四种方式分别是: forkey,valueinpairs(tbtest)do XXX end forkey,valueinipairs(tbtest)do XXX end for...

ahuang8435的专栏 84

luatable 的四种遍历方式

使用ipairs 迭代器材遍历: 1.将所有table中的key值排列。 2.找到key唯1的索引。 3.向后递增1,如果key值存在,则结束遍历。 总结: 这种迭代器是用用来遍历数组的 使用pairs 迭代器遍历: 1.将所有table中的key值排列 2.遍历table中...

qq_17433125的博客 1578

Lua_table中常用的四种遍历方式

--第一种遍历方式: --[[ for key, value in ipairs(table) do end 注:这种方式的遍历只会从key为1的地方开始,一直以key递增1的顺序来遍历,若找到一个递增是1的key就结束遍历,无论后面是否仍然是顺序的key。请看下面例子: ]] --Sample_1: local tab1 = { [1] = 1, [2] = 2, [3] =

青青子衿 1万+

Lua table.remove()

删除.png 前言# 看到这个清晰的大叉号图片时我们禁会想起关闭、删除等含义,今天我们总结的函数恰恰和删除有关们,这个函数也是表table中的一个,负责表中数据元素的删除,我们具体来看一下这个函数的用法。 内容# table.remove()## 原型:table.remove (tab_table [, pos]) 解释:在表tab_table的pop位置删除元素,并且的必要时...

chqj_163的专栏 9343

lua遍历

       项目中有很多配置表的数据解析出来都是用表接收的,所以经常会用到对表的遍历。可是由于表中存储的数据的结构一样,所以遍历的方式也是一样的,有简单的表,直接存储连续数组的,也有存储list的,也有存储字典的,因为数据解析都是从C#解析过来的一:简单数字型的,这种是最简单的,内部存储的是简单的数字local table1 = {-1,15,10,20}; for k,v in pair...

Tong1993222的博客 4403

Lua table遍历

工作中,栽了一个“坑”,特此备录。 【1】遍历table1,每次结果可能都同 -- 获取value local table_1 = {id="101", name="wang", sage="18", addr="xian"} for k, v in pairs(table_1) do print(k, v) end 每次结果可能都同 【2】遍历table2,每...

weixin_30848775的博客 362

lua 遍历table

lua中有四种主要的遍历一个table的方法。 第一种方法: 1 for key, value in pairs(testtb) do 2 xxxx 3 end 这种方法是按照key哈希后的顺序遍历的。比如下面代码的输出,并是按顺序的 testtb = { [1] = 1; [2] = 2; [3] = 3; [5] = 5...

weixin_30520015的博客 260

Lua table 顺序遍历

Lua中, 如何遍历一个Table元素,主要有两种类型: 1.pairs:下标从1开始,并且是连续的才可以使用 2.ipairs:是无序的示例 local t = { [10] = 1, [20] = 2, [30] = 3, [40] = 4, [50] = 5, [60] = 6 } for k,v in pai...

weixin_30565199的博客 962

lua 基础语法之 for 循环遍历 三种方式

这种遍历表的方式会从数字索引(interger key)1开始遍历,如果下一个数字索引存在,则结束。这种遍历表的方式会遍历所有的键值对。

qq_57693806的博客 3005

Lua遍历嵌套table

摘要:本代码展示了如何递归遍历嵌套的Lua表(table)。定义了一个TraverseTable函数,可处理多层级嵌套表结构,通过判断元素类型决定是否递归遍历遍历时输出每个元素的键值对,并通过缩进空格表示层级关系。示例创建了一个包含嵌套结构的表,并调用TraverseTable函数进行遍历演示。该代码适用于需要深度遍历和分析Lua表结构的场景。

weixin_47220117的博客 337

Lua Table(表)

Lua table 是一种动态数组,可以存储任意类型的值,包括数字、字符串、函数甚至其他 tabletableLua 中唯一的数据结构,但它的灵活性和强大功能使其成为处理复杂数据结构的理想选择。Lua table 是一种强大且灵活的数据结构,可以用于存储和处理各种类型的数据。通过掌握 table 的基本用法和高级特性,可以更高效地使用 Lua 进行编程。

wjs2024的博客 765

lua中的4 种常用遍历

当我在工作中使用lua进行开发时,发现在lua中有4种方式遍历一个table,当然,从本质上来说其实都一样,只是形式同,这四种方式分别是: for key, value in pairs(tbtest) do XXX end for key, value in ipairs(tbtest) do XXX end for i=1, #(tbtest) do XXX

u011488256的博客 5091

lua顺序遍历table

lua有多种遍历table的方法,比如ipairs,pairs,拿到table的长度#table等等。这几种遍历的方式遇到同的情况遍历出来的结果也一样,具体是怎么回事呢,下面就将所有的情况都分析一遍。 首先就是遍历的元素是K,V的形式,在lua中如果被遍历的元素是键值对的形式,那么lua会顺序遍历元素。看几个示例一目了然。 1. table中都是一个个的单元素 --table中都是一个个...

积累成永恒 1万+

SOLIDWORKS弹簧插件

SOLIDWORKS弹簧插件画弹簧的插件

上一篇: Lua库之时间和日期操作
下一篇: LUA脚本的loadstring的用法
nnsword
博客等级 码龄19年 186粉丝 372原创
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值