Lua简易入门
2015-01-12 09:45
309 查看
| ||
浅析Lua中table的遍历和删除(转)
当我在工作中使用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这种方式:
先看效果:
tbtest = {
[1] = 1,
[2] = 2,
[3] = 3,
[4] = 4,
}
for key, value in pairs(tbtest) do
print(value)
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不连续为止。
tbtest = {
[1] = 1,
[2] = 2,
[3] = 3,
[5] = 5,
}
for k,v in ipairs(tbtest) do
print(v)
end
只会打印1,2,3。而5则不会显示。
local tbtest = {
[2] = 2,
[3] = 3,
[5] = 5,
}
for k,v in ipairs(tbtest) do
print(v)
end
这样就一个都不会打印。
第三种遍历方式有一种神奇的符号'#',这个符号的作用是是获取table的长度,比如:
tbtest = {
[1] = 1,
[2] = 2,
[3] = 3,
}
print(#(tbtest))
打印的就是3
tbtest = {
[1] = 1,
[2] = 2,
[6] = 6,
}
print(#(tbtest))
这样打印的就是2,而且和table内的定义顺序没有关系,无论你是否先定义的key为6的值,‘#’都会查找key为1的值开始。
如果table的定义是这样的:
tbtest = {
["a"] = 1,
[2] = 2,
[3] = 3,
}
print(#(tbtest))
那么打印的就是0了。因为‘#’没有找到key为1的值。同样:
tbtest = {
[“a”] = 1,
[“b”] = 2,
[“c”] = 3,
}
print(#(tbtest))
打印的也是0
所以,for i=1, #(tbtest) do这种遍历,只能遍历当tbtest中存在key为1的value时才会出现结果,而且是按照key从1开始依次递增1的顺序来遍历,找到一个递增不是1的时候就结束不再遍历,无论后面是否仍然是顺序的key,比如:
table.maxn获取的只针对整数的key,字符串的key是没办法获取到的,比如:
tbtest = {
[1] = 1,
[2] = 2,
[3] = 3,
}
print(table.maxn(tbtest))
tbtest = {
[6] = 6,
[1] = 1,
[2] = 2,
}
print(table.maxn(tbtest))
这样打印的就是3和6,而且和table内的定义顺序没有关系,无论你是否先定义的key为6的值,table.maxn都会获取整数型key中的最大值。
如果table的定义是这样的:
tbtest = {
["a"] = 1,
[2] = 2,
[3] = 3,
}
print(table.maxn(tbtest))
那么打印的就是3了。如果table是:
tbtest = {
[“a”] = 1,
[“b”] = 2,
[“c”] = 3,
}
print(table.maxn(tbtest))
print(#(tbtest))
那么打印的就全部是0了。
换句话说,事实上因为lua中table的构造表达式非常灵活,在同一个table中,你可以随意定义各种你想要的内容,比如:
tbtest = {
[1] = 1,
[2] = 2,
[3] = 3,
["a"] = 4,
["b"] = 5,
}
同时由于这个灵活性,你也没有办法获取整个table的长度,其实在coding的过程中,你会发现,你真正想要获取整个table长度的地方几乎没有,你总能采取一种非常巧妙的定义方式,把这种需要获取整个table长度的操作避免掉,比如:
tbtest = {
tbaaa = {
[1] = 1,
[2] = 2,
[3] = 3,
},
["a"] = 4,
["b"] = 5,
}
你可能会惊讶,上面这种table该如何遍历呢?
for k, v in pairs(tbtest) do
print(k, v)
end
输出是:a 4 b 5 tbaaa table:XXXXX。
由此你可以看到,其实在table中定义一个table,这个table的名字就是key,对应的内容其实是table的地址。
当然,如果你用
for k, v in ipairs(tbtest) do
print(k,v)
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每一个元素都会查找一遍,实际上大多数元素都是不存在的,比如:
tbtest = {
[1] = 1,
[10000] = 2,
}
local count = 0
for i=1, table.maxn(tbtest) do
count = count + 1
print(tbtest[i])
end
print(count)
你会看到打印结果是多么的坑爹,只有1和10000是有意义的,其他的全是nil,而且count是10000。耗时非常久。一般我不这么遍历。但是有一种情况下又必须这么遍历,这个在我的工作中还真的遇到了,这是后话,等讲完了再谈。
for k, v in pairs(tbtest) do
这个是唯一一种可以保证遍历tbtest中每一个元素的方式,别高兴的太早,这种遍历也有它自身的缺点,就是遍历的顺序不是按照tbtest定义的顺序来遍历的,这个前面讲到过,当然,对于不需要顺序遍历的用法,这个是唯一可靠的遍历方式。
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定义成这个样子:
tbtestAward = {
[8] = 1,
[16] = 3,
}
这个代表,1~8给奖励1,9~16给奖励3。这样定义的好处是奖励我只需要写一次(这里的奖励用数字做了简化,实际上奖励也是一个大的table,里面还有非常复杂的结构)。然后我就遇到一个问题,即我需要根据周序数,或者是排名序数来确定给哪一种奖励,比如当前周序数是5,那么我应该给我定义好的key为8的那一档奖励,或者当前周序数是15,那么我应该给奖励3。由此读者看出,其实我定义的key是一个分界,小于这个key而大于上一个key,那么就给这个key的奖励,这就是我判断的条件。逻辑上没有问题,但是lua的遍历方式却把我狠狠地坑了一把。读者可以自己想一想我上面介绍的4种遍历方式,该用哪一种来实现我的这种需求呢?这个函数的大致框架如下:
function GetAward(nSeq)
for 遍历整个奖励表 do
if 满足key的条件 then
return 返回对应奖励的key
end
end
return nil
end
我也不卖关子了,分别来说一说吧,首先因为我的key不是连续的,而且没有key为1的值,所以ipairs和'#'遍历是没用的。这种情况下理想的遍历貌似是pairs,因为它会遍历我的每一个元素,但是读者不要忘记了,pairs遍历并非是按照我定义的顺序来遍历,如果我真的使用的条件是:序数nSeq小于这个key而大于上一个key,那么就返回这个key。那么我无法保证程序执行的正确性,因为key的顺序有可能是乱的,也就是有可能先遍历到的是key为16的值,然后才是key为8的值。
这么看来我只剩下table.maxn这么一种方式了,于是我写下了这种代码:
for i=1, table.maxn(tbtestAward) do
if tbtestAward[i] ~= nil then
if nSeq <= i then
return i
end
end
end
这么写效率确实低下,因为实际上还是遍历了从key为1开始直到key为table.maxn中间的每一个值,不过能够满足我上面的要求。当时我是这么实现的,因为这个奖励表会不断的发生变化,这样我每次修改只需要修改这个奖励表就能够满足要求了,后来我想了想,觉得其实我如果自己再定义一个序数转换成对应的奖励数种类的表就可以避免这种坑爹的操作了,不过如果奖励发生修改,我需要统一排查的地方就不止这个奖励表了,权衡再三,我还是没有改,就这么写了。没办法,不断变化的需求已经把我磨练的忘记了程序的最高理想。我甚至愿意牺牲算法的效率而去追求改动的稳定性。在此哀悼程序员的无奈。我这种时间换空间的做法确实不知道好不好。
后来我在《Programming In Lua》中看到了一个神奇的迭代器,使用它就可以达到我想要的这种遍历方式,而且不需要去遍历那些不存在的key。它的方法是把你所需要遍历的table里的key按照遍历顺序放到另一个临时的table中去,这样只需要遍历这个临时的table按顺序取出原table中的key就可以了。如下:
首先定义一个迭代器:
function pairsByKeys(t)
local a = {}
for n in pairs(t) do
a[#a+1] = n
end
table.sort(a)
local i = 0
return function()
i = i + 1
return a[i], t[a[i]]
end
end
然后在遍历的时候使用这个迭代器就可以了,table同上,遍历如下:
for key, value in pairsByKeys(tbtestAward) do
if nSeq <= key then
return key
end
end
并且后来我发现有了这个迭代器,我根本不需要先做一步获取是哪一档次的奖励的操作,直接使用这个迭代器进行发奖就可以了。大师就是大师,我怎么就没想到呢!
还有些话我还没有说,比如上面数值型遍历也并非是像看起来那样进行遍历的,比如下面的遍历:
tbtest = {
[1] = 1,
[2] = 2,
[3] = 3,
[5] = 5,
}
for i=1, #(tbtest) do
print(tbtest[i])
end
打印的顺序是:1,2,3。不会打印5,因为5已经不在table的数组数据块中了,我估计是被放到了hash数据块中,但是当我修改其中的一些key时,比如:
tbtest = {
[1] = 1,
[2] = 2,
[4] = 4,
[5] = 5,
}
for i=1, #(tbtest) do
print(tbtest[i])
end
打印的内容却是:1,2,nil,4,5。这个地方又遍历到了中间没有的key值,并且还能继续遍历下去。我最近正在看lua源码中table的实现部分,已经明白了是怎么回事,不过我想等我能够更加清晰的阐述lua中table的实现过程了再向大家介绍。用我师傅的话说就是不要使用一些未定义的行为方法,避免在工作中出错,不过工作外,我还是希望能明白未定义的行为中那些必然性,o(︶︿︶)o 唉!因果论的孩子伤不起。等我下一篇博文分析lua源码中table的实现就能够更加清晰的说明这些了。
---------------------------------------------------------------------------------------------分割线-----------------------------------------------------------------------------------------------------------
---------------------------------------------------------------------------------------------分割线-----------------------------------------------------------------------------------------------------------
---------------------------------------------------------------------------------------------分割线-----------------------------------------------------------------------------------------------------------
原文 http://childhood.logdown.com/posts/200499/lua-table-how-to-safely-remove-an-element
在Lua中,table如何安全的移除元素这点挺重要,因为如果不小心,会没有正确的移除,造成内存泄漏。
[title3]
引子[/title3]
比如有些朋友常常这么做,大家看有啥问题
将test表中的偶数移除掉<span class="kd">local</span> <span class="n">test</span> <span class="o">=</span> <span class="p">{</span> <span class="mi"><span class="number" style="color:rgb(0,153,153)">2</span></span><span class="p">,</span> <span class="mi"><span class="number" style="color:rgb(0,153,153)">3</span></span><span class="p">,</span> <span class="mi"><span class="number" style="color:rgb(0,153,153)">4</span></span><span class="p">,</span> <span class="mi"><span class="number" style="color:rgb(0,153,153)">8</span></span><span class="p">,</span> <span class="mi"><span class="number" style="color:rgb(0,153,153)">9</span></span><span class="p">,</span> <span class="mi"><span class="number" style="color:rgb(0,153,153)">100</span></span><span class="p">,</span> <span class="mi"><span class="number" style="color:rgb(0,153,153)">20</span></span><span class="p">,</span> <span class="mi"><span class="number" style="color:rgb(0,153,153)">13</span></span><span class="p">,</span> <span class="mi"><span class="number" style="color:rgb(0,153,153)">15</span></span><span class="p">,</span> <span class="mi"><span class="number" style="color:rgb(0,153,153)">7</span></span><span class="p">,</span> <span class="mi"><span class="number" style="color:rgb(0,153,153)">11</span></span><span class="p">}</span>
<span class="k"><span class="keyword" style="font-weight:bold">for</span></span> <span class="n">i</span><span class="p">,</span> <span class="n">v</span> <span class="k"><span class="keyword" style="font-weight:bold">in</span></span> <span class="nb">ipairs</span><span class="p">(</span> <span class="n">test</span> <span class="p">)</span> <span class="k"><span class="keyword" style="font-weight:bold">do</span></span>
<span class="indent"> </span><span class="k"><span class="keyword" style="font-weight:bold">if</span></span> <span class="n">v</span> <span class="o">%</span> <span class="mi"><span class="number" style="color:rgb(0,153,153)">2</span></span> <span class="o">==</span> <span class="mi"><span class="number" style="color:rgb(0,153,153)">0</span></span> <span class="k"><span class="keyword" style="font-weight:bold">then</span></span>
<span class="indent"> </span><span class="indent"> </span><span class="nb">table.remove</span><span class="p">(</span><span class="n">test</span><span class="p">,</span> <span class="n">i</span><span class="p">)</span>
<span class="indent"> </span><span class="k"><span class="keyword" style="font-weight:bold">end</span></span>
<span class="k"><span class="keyword" style="font-weight:bold">end</span></span>
<span class="k"><span class="keyword" style="font-weight:bold">for</span></span> <span class="n">i</span><span class="p">,</span> <span class="n">v</span> <span class="k"><span class="keyword" style="font-weight:bold">in</span></span> <span class="nb">ipairs</span><span class="p">(</span> <span class="n">test</span> <span class="p">)</span> <span class="k"><span class="keyword" style="font-weight:bold">do</span></span>
<span class="indent"> </span><span class="nb">print</span><span class="p">(</span><span class="n">i</span> <span class="o">..</span> <span class="s2"><span class="string" style="color:rgb(221,17,68)">"===="</span></span> <span class="o">..</span> <span class="n">v</span><span class="p">)</span>
<span class="k"><span class="keyword" style="font-weight:bold">end</span></span>
打印结果:
<span class="mi">1</span><span class="setting"><span class="o">=<span class="value">===</span></span><span class="value"><span class="mi"><span class="number" style="color:rgb(0,153,153)">3</span></span></span><span class="mi"></span></span><span class="mi"></span>
<span class="mi">2</span><span class="setting"><span class="o">=<span class="value">===</span></span><span class="value"><span class="mi"><span class="number" style="color:rgb(0,153,153)">8</span></span></span><span class="mi"></span></span><span class="mi"></span>
<span class="mi">3</span><span class="setting"><span class="o">=<span class="value">===</span></span><span class="value"><span class="mi"><span class="number" style="color:rgb(0,153,153)">9</span></span></span><span class="mi"></span></span><span class="mi"></span>
<span class="mi">4</span><span class="setting"><span class="o">=<span class="value">===</span></span><span class="value"><span class="mi"><span class="number" style="color:rgb(0,153,153)">20</span></span></span><span class="mi"></span></span><span class="mi"></span>
<span class="mi">5</span><span class="setting"><span class="o">=<span class="value">===</span></span><span class="value"><span class="mi"><span class="number" style="color:rgb(0,153,153)">13</span></span></span><span class="mi"></span></span><span class="mi"></span>
<span class="mi">6</span><span class="setting"><span class="o">=<span class="value">===</span></span><span class="value"><span class="mi"><span class="number" style="color:rgb(0,153,153)">15</span></span></span><span class="mi"></span></span><span class="mi"></span>
<span class="mi">7</span><span class="setting"><span class="o">=<span class="value">===</span></span><span class="value"><span class="mi"><span class="number" style="color:rgb(0,153,153)">7</span></span></span><span class="mi"></span></span><span class="mi"></span>
<span class="mi">8</span><span class="setting"><span class="o">=<span class="value">===</span></span><span class="value"><span class="mi"><span class="number" style="color:rgb(0,153,153)">11</span></span></span><span class="mi"></span></span><span class="mi"></span>
<span class="p"><span class="title" style="color:rgb(153,0,0); font-weight:bold">[</span></span><span class="title" style="color:rgb(153,0,0); font-weight:bold"><span class="n">Finished</span> <span class="k">in</span> <span class="mi">0</span><span class="p">.</span><span class="mi">0</span><span class="n">s</span><span class="p">]</span></span><span class="p"></span>
有问题吧,20怎么还在?这就是在遍历中删除导致的。
[title3]
如何做呢?[/title3]
Let's get started!<span class="kd">local</span> <span class="n">test</span> <span class="o">=</span> <span class="p">{</span> <span class="s1"><span class="string" style="color:rgb(221,17,68)">'a'</span></span><span class="p">,</span><span class="s1"><span class="string" style="color:rgb(221,17,68)">'b'</span></span><span class="p">,</span><span class="s1"><span class="string" style="color:rgb(221,17,68)">'c'</span></span><span class="p">,</span><span class="s1"><span class="string" style="color:rgb(221,17,68)">'d'</span></span><span class="p">,</span><span class="s1"><span class="string" style="color:rgb(221,17,68)">'e'</span></span><span class="p">,</span><span class="s1"><span class="string" style="color:rgb(221,17,68)">'f'</span></span><span class="p">,</span><span class="s1"><span class="string" style="color:rgb(221,17,68)">'g'</span></span><span class="p">,</span><span class="s1"><span class="string" style="color:rgb(221,17,68)">'h'</span></span><span class="p">,</span><span class="s1"><span class="string" style="color:rgb(221,17,68)">'i'</span></span><span class="p">,</span><span class="s1"><span class="string" style="color:rgb(221,17,68)">'j'</span></span><span class="p">,</span><span class="s1"><span class="string" style="color:rgb(221,17,68)">'k'</span></span><span class="p">,</span><span class="s1"><span class="string" style="color:rgb(221,17,68)">'l'</span></span><span class="p">,</span><span class="s1"><span class="string" style="color:rgb(221,17,68)">'m'</span></span><span class="p">,</span><span class="s1"><span class="string" style="color:rgb(221,17,68)">'n'</span></span><span class="p">,</span><span class="s1"><span class="string" style="color:rgb(221,17,68)">'o'</span></span><span class="p">,</span><span class="s1"><span class="string" style="color:rgb(221,17,68)">'p'</span></span> <span class="p">}</span>
<span class="kd">local</span> <span class="n">remove</span> <span class="o">=</span> <span class="p">{</span> <span class="n">a</span> <span class="o">=</span> <span class="kc"><span class="keyword" style="font-weight:bold">true</span></span><span class="p">,</span> <span class="n">b</span> <span class="o">=</span> <span class="kc"><span class="keyword" style="font-weight:bold">true</span></span><span class="p">,</span> <span class="n">c</span> <span class="o">=</span> <span class="kc"><span class="keyword" style="font-weight:bold">true</span></span><span class="p">,</span> <span class="n">e</span> <span class="o">=</span> <span class="kc"><span class="keyword" style="font-weight:bold">true</span></span><span class="p">,</span> <span class="n">f</span> <span class="o">=</span> <span class="kc"><span class="keyword" style="font-weight:bold">true</span></span><span class="p">,</span> <span class="n">p</span> <span class="o">=</span> <span class="kc"><span class="keyword" style="font-weight:bold">true</span></span> <span class="p">}</span>
<span class="kd">local</span> <span class="k"><span class="function"><span class="keyword" style="font-weight:bold">function</span></span></span><span class="function"> <span class="nf"><span class="title" style="color:rgb(153,0,0); font-weight:bold">dump</span></span><span class="p"><span class="params">(</span></span><span class="params"><span class="n">table</span><span class="p">)</span></span><span class="p"></span>
<span class="k"><span class="title" style="color:rgb(153,0,0); font-weight:bold">for</span></span> <span class="n"><span class="title" style="color:rgb(153,0,0); font-weight:bold">k</span></span><span class="p">,</span> <span class="n"><span class="title" style="color:rgb(153,0,0); font-weight:bold">v</span></span> <span class="k"><span class="title" style="color:rgb(153,0,0); font-weight:bold">in</span></span> <span class="nb"><span class="title" style="color:rgb(153,0,0); font-weight:bold">pairs</span></span><span class="p"><span class="params">(</span></span><span class="params"> <span class="n">table</span> <span class="p">)</span></span><span class="p"></span> <span class="k"><span class="title" style="color:rgb(153,0,0); font-weight:bold">do</span></span>
<span class="nb"><span class="title" style="color:rgb(153,0,0); font-weight:bold">print</span></span><span class="p"><span class="params">(</span></span><span class="params"><span class="n">k</span><span class="p">)</span></span><span class="p"></span>
<span class="nb"><span class="title" style="color:rgb(153,0,0); font-weight:bold">print</span></span><span class="p"><span class="params">(</span></span><span class="params"><span class="n">v</span><span class="p">)</span></span><span class="p"></span>
<span class="nb"><span class="title" style="color:rgb(153,0,0); font-weight:bold">print</span></span><span class="p"><span class="params">(</span></span><span class="params"><span class="s2"><span class="string" style="color:rgb(221,17,68)">"*********"</span></span><span class="p">)</span></span><span class="p"></span>
<span class="k"><span class="title" style="color:rgb(153,0,0); font-weight:bold">end</span></span>
<span class="k"><span class="title" style="color:rgb(153,0,0); font-weight:bold">end</span></span></span><span class="k"></span>
说明:一般我们不在循环中删除,在循环中删除会造成一些错误。这是可以建立一个remove表用来标记将要删除的,如上面例子,把将要删除的标记为true
[title3]
方法1 从后往前删除[/title3]
<span class="k"><span class="keyword" style="font-weight:bold">for</span></span> <span class="n">i</span> <span class="o">=</span> <span class="o"><span class="comment" style="color:rgb(153,153,136); font-style:italic">#</span></span><span class="comment" style="color:rgb(153,153,136); font-style:italic"><span class="n">test</span><span class="p">,</span> <span class="mi">1</span><span class="p">,</span> <span class="o">-</span><span class="mi">1</span> <span class="k">do</span></span><span class="k"></span>
<span class="k"><span class="keyword" style="font-weight:bold">if</span></span> <span class="n">remove</span><span class="p">[</span><span class="n">test</span><span class="p">[</span><span class="n">i</span><span class="p">]]</span> <span class="k"><span class="keyword" style="font-weight:bold">then</span></span>
<span class="nb">table.remove</span><span class="p">(</span><span class="n">test</span><span class="p">,</span> <span class="n">i</span><span class="p">)</span>
<span class="k"><span class="keyword" style="font-weight:bold">end</span></span>
<span class="k"><span class="keyword" style="font-weight:bold">end</span></span>
<span class="n">dump</span><span class="p">(</span><span class="n">test</span><span class="p">)</span>
为什么不从前往后,朋友们可以测试,table.remove操作后,后面的元素会往前移位,这时候后续的删除索引对应的元素已经不是之前的索引对应的元素了。
[title3]
方法2 while删除[/title3]
<span class="kd">local</span> <span class="n">i</span> <span class="o">=</span> <span class="mi"><span class="number" style="color:rgb(0,153,153)">1</span></span>
<span class="k"><span class="keyword" style="font-weight:bold">while</span></span> <span class="n">i</span> <span class="o"><=</span> <span class="o"><span class="comment" style="color:rgb(153,153,136); font-style:italic">#</span></span><span class="comment" style="color:rgb(153,153,136); font-style:italic"><span class="n">test</span> <span class="k">do</span></span><span class="k"></span>
<span class="indent"> </span><span class="k"><span class="keyword" style="font-weight:bold">if</span></span> <span class="n">remove</span><span class="p">[</span><span class="n">test</span><span class="p">[</span><span class="n">i</span><span class="p">]]</span> <span class="k"><span class="keyword" style="font-weight:bold">then</span></span>
<span class="indent"> </span><span class="indent"> </span><span class="nb">table.remove</span><span class="p">(</span><span class="n">test</span><span class="p">,</span> <span class="n">i</span><span class="p">)</span>
<span class="indent"> </span><span class="k"><span class="keyword" style="font-weight:bold">else</span></span>
<span class="indent"> </span><span class="indent"> </span><span class="n">i</span> <span class="o">=</span> <span class="n">i</span> <span class="o">+</span> <span class="mi"><span class="number" style="color:rgb(0,153,153)">1</span></span>
<span class="indent"> </span><span class="k"><span class="keyword" style="font-weight:bold">end</span></span>
<span class="k"><span class="keyword" style="font-weight:bold">end</span></span>
<span class="n">dump</span><span class="p">(</span><span class="n">test</span><span class="p">)</span>
[title3]
方法3 quick中提供的removeItem[/title3]
<span class="k">function</span> <span class="nc">table</span><span class="p">.</span><span class="nf">removeItem</span><span class="p">(</span><span class="n">list</span><span class="p">,</span> <span class="n">item</span><span class="p">,</span> <span class="n">removeAll</span><span class="p">)</span>
<span class="indent"> </span><span class="kd">local</span> <span class="n">rmCount</span> <span class="o">=</span> <span class="mi"><span class="number" style="color:rgb(0,153,153)">0</span></span>
<span class="indent"> </span><span class="k"><span class="keyword" style="font-weight:bold">for</span></span> <span class="n">i</span> <span class="o">=</span> <span class="mi"><span class="number" style="color:rgb(0,153,153)">1</span></span><span class="p">,</span> <span class="o"><span class="comment" style="color:rgb(153,153,136); font-style:italic">#</span></span><span class="comment" style="color:rgb(153,153,136); font-style:italic"><span class="n">list</span> <span class="k">do</span></span><span class="k"></span>
<span class="indent"> </span><span class="indent"> </span><span class="k"><span class="keyword" style="font-weight:bold">if</span></span> <span class="n">list</span><span class="p">[</span><span class="n">i</span> <span class="o">-</span> <span class="n">rmCount</span><span class="p">]</span> <span class="o">==</span> <span class="n">item</span> <span class="k"><span class="keyword" style="font-weight:bold">then</span></span>
<span class="indent"> </span><span class="indent"> </span><span class="indent"> </span><span class="nb">table.remove</span><span class="p">(</span><span class="n">list</span><span class="p">,</span> <span class="n">i</span> <span class="o">-</span> <span class="n">rmCount</span><span class="p">)</span>
<span class="indent"> </span><span class="indent"> </span><span class="indent"> </span><span class="k"><span class="keyword" style="font-weight:bold">if</span></span> <span class="n">removeAll</span> <span class="k"><span class="keyword" style="font-weight:bold">then</span></span>
<span class="indent"> </span><span class="indent"> </span><span class="indent"> </span><span class="indent"> </span><span class="n">rmCount</span> <span class="o">=</span> <span class="n">rmCount</span> <span class="o">+</span> <span class="mi"><span class="number" style="color:rgb(0,153,153)">1</span></span>
<span class="indent"> </span><span class="indent"> </span><span class="indent"> </span><span class="k"><span class="keyword" style="font-weight:bold">else</span></span>
<span class="indent"> </span><span class="indent"> </span><span class="indent"> </span><span class="indent"> </span><span class="k"><span class="keyword" style="font-weight:bold">break</span></span>
<span class="indent"> </span><span class="indent"> </span><span class="indent"> </span><span class="k"><span class="keyword" style="font-weight:bold">end</span></span>
<span class="indent"> </span><span class="indent"> </span><span class="k"><span class="keyword" style="font-weight:bold">end</span></span>
<span class="indent"> </span><span class="k"><span class="keyword" style="font-weight:bold">end</span></span>
<span class="k"><span class="keyword" style="font-weight:bold">end</span></span>
<span class="k"><span class="keyword" style="font-weight:bold">for</span></span> <span class="n">k</span><span class="p">,</span> <span class="n">v</span> <span class="k"><span class="keyword" style="font-weight:bold">in</span></span> <span class="nb">pairs</span><span class="p">(</span> <span class="n">remove</span> <span class="p">)</span> <span class="k"><span class="keyword" style="font-weight:bold">do</span></span>
<span class="indent"> </span><span class="n">table</span><span class="p">.</span><span class="n">removeItem</span><span class="p">(</span><span class="n">test</span><span class="p">,</span> <span class="n">k</span><span class="p">)</span>
<span class="k"><span class="keyword" style="font-weight:bold">end</span></span>
Lua语言基础汇总(12)-- Lua中的面向对象编程
果冻想2014-08-11 14:08:322087 次阅读简单说说Lua中的面向对象
Lua中的table就是一种对象,看以下一段简单的代码:
使用self参数是所有面向对象语言的一个核心。大多数面向对象语言都对程序员隐藏了self参数,从而使得程序员不必显示地声明这个参数。Lua也可以,当我们在定义函数时,使用了冒号,则能隐藏该参数,那么上述代码使用冒号来改下,就是下面这个样子了。
这里乱乱的讲了一些Lua中的东西,主要还是说了table是一个不一样的东西,还有self。接下来,就正式进入面向对象的世界。不要忘了,上面总结的东西是非常有用的。
类
类是什么?一个类就是一个创建对象的模具。例如C++中,每个对象都是某个特定类的实例。在C++中,如果一个类没有进行实例化,那这个类中对应的操作,基本就是一堆“没有用”的代码;而Lua则不一样,即使你不实例化一个“类”,你照样也可以使用“类”名直接调用它的方法(对于C++,请忽视静态的方法);这说明Lua中的“类”的概念与C++这种高级语言中类的概念还是有差别的。在Lua中则没有类的概念,而我们都是通过Lua现有的支持,去模拟类的概念。在Lua中,要表示一个类,只需创建一个专用作其他对象的原型(prototype)。原型也是一种常规的对象,也就是说我们可以直接通过原型去调用对应的方法。当其它对象(类的实例)遇到一个未知操作时,原型会先查找它。
在Lua中实现原型是非常简单的,比如有两个对象a和b,要让b作为a的原型,只需要以下代码就可以完成:
继承不仅可以用于方法,还可以作用于字段。因此,一个类不仅可以提供方法,还可以为实例中的字段提供默认值。看以下代码:
继承
由于类也是对象(准确地说是一个原型),它们也可以从其它类(原型)获得(继承)方法。这种行为就是继承,可以很容易的在Lua中实现。现在我们有一个类(原型,其实在Lua中说类这个概念,还是很别扭的,毕竟用C++的脑袋去想,还是觉的有点奇怪的。)CA:
多重继承
说到多重继承,我在写C++代码的时候也用的很少,一般都是使用组合的方式解决的,对于“组合”这个概念不明白的朋友,可以阅读我的设计模式系列的文章。既然说到了Lua中的多重继承,那也总结一下,顺便开拓一下视野和知识面。
实现单继承时,依靠的是为子类设置metatable,设置其metatable为父类,并将父类的__index设置为其本身的技术实现的。而多继承也是一样的道理,在单继承中,如果子类中没有对应的字段,则只需要在一个父类中寻找这个不存在的字段;而在多重继承中,如果子类没有对应的字段,则需要在多个父类中寻找这个不存在的字段。
就像上图表示一样,Lua会在多个父类中逐个的搜索display字段。这样,我们就不能像单继承那样,直接指定__index为某个父类,而是应该指定__index为一个函数,在这个函数中指定搜索不存在的字段的规则。这样便可实现多重继承。这里就出现了两个需要去解决的问题:
1. 保存所有的父类;
2. 指定一个搜索函数来完成搜索任务。
对于以上的多重继承,我们来看一段头疼的代码:
使用createClass创建了一个类(原型),将CA和CB设置为这个类(原型)的父类(原型);在创建的这个类(原型)中,设置了该类的__index为一个search函数,在这个search函数中寻找在创建的类中没有的字段;
创建的新类中,有一个构造函数new;这个new和之前的单继承中的new区别不大,很好理解;
调用new构造函数,创建一个实例对象,该实例对象有一个name字段;
调用object:setName(“JellyThink”)语句,设置一个新的名字;但是在objectC中没有这个字段,怎么办?好了,去父类找,先去CA找,一下子就找到了,然后就调用了这个setName,setName中的self指向的是objectC;设置以后,就相当于修改了objectC字段的name值;
调用objectC:getName(),objectC还是没有这个字段。找吧,CA也没有,那就接着找,在CB中找到了,就调用getName,在getName中的self指向的是objectC。所以,在objectC:getName中返回了objectC中name的值,就是“JellyThink”。
还有什么?什么也没有了,对于多重继承,貌似看起来很难,很麻烦,其实也就这么点东西。不懂的话,再来一遍。
我拿什么保护你
我们都知道,在C++或Java中,对于类中的成员函数或变量都有访问权限的。public,protected和private这几个关键字还认识吧。那么在Lua中呢?Lua中是本身就是一门“简单”的脚本语言,本身就不是为了大型项目而生的,所以,它的语言特性中,本身就没有带有这些东西,那如果非要用这样的保护的东西,该怎么办?我们还是“曲线救国”。思想就是通过两个table来表示一个对象。一个table用来保存对象的私有数据;另一个用于对象的操作。对象的实际操作时通过第二个table来实现的。为了避免为授权的访问,保存对象的私有数据的表不保存在其它的table中,而只是保存在方法的closure中。看一段代码:
总结
这篇文章对Lua中的“面向对象”进行了一些简单的总结,本来Lua就很简单,我们只是使用了Lua本身的特性去实现一些更高大上的特性,这样没有什么不好,有的时候也没有什么好。要不要用面向对象的这些特性,在具体项目中具体分析,至于如何理解面向对象的概念,不是这里的重点。Lua的面向对象,总结到此为止,以后有了实际的项目应用,接着总结,这篇基础篇的文章,希望对大家有用。
**************************************************
理解lua 语言中的点、冒号与self lua 中 require
2013年07月29日 ⁄ 综合 ⁄ 共 603字 ⁄ 字号 小 中 大 ⁄ 评论关闭lua编程中,经常遇到函数的定义和调用,有时候用点号调用,有时候用冒号调用,这里简单的说明一下原理。
girl = {money = 200} function girl.goToMarket(girl ,someMoney) girl.money = girl.money - someMoney end girl.goToMarket(girl ,100) print(girl.money)
可以看出,这里进行了方法的点号定义和点号调用。
boy = {money = 200} function boy:goToMarket(someMoney) self.money = self.money - someMoney end boy:goToMarket(100) print(boy.money)
这里进行了冒号定义和冒号调用。
以上的打印结果都是100。
可以看出,冒号定义和冒号调用其实跟上面的效果一样,只是把第一个隐藏参数省略了,而该参数self指向调用者自身
当然了,我们也可以点号定义冒号调用,或者冒号定义点号调用
如:
boy = {money = 200} function boy.goToMarket(self ,someMoney) self.money = self.money - someMoney end boy:goToMarket(100) print(boy.money)
总结:冒号只是起了省略第一个参数self的作用,该self指向调用者本身,并没有其他特殊的地方