设为首页收藏本站|繁體中文

Project1

 找回密码
 注册会员
搜索
查看: 3269|回复: 15
打印 上一主题 下一主题

[通用发布] 试验 ruby迭代器奇怪的漏洞bug

[复制链接]

Lv1.梦旅人

梦石
0
星屑
50
在线时间
56 小时
注册时间
2012-10-1
帖子
36
跳转到指定楼层
1
发表于 2012-10-9 23:00:53 | 只看该作者 |只看大图 回帖奖励 |正序浏览 |阅读模式

加入我们,或者,欢迎回来。

您需要 登录 才可以下载或查看,没有帐号?注册会员

x
ruby当中,数组和hash数据结构都可以使用迭代器。假设一个数组,遍历每一个元素,其中每访问一个元素,调用这个元素的一个方法update,使在遍历过程中又删除该数组中的某些元素,这个遍历结果会怎么样。

在Game_Map当中设置usr_events并初始化为[],update_events方法中迭代usr_events。
RUBY 代码复制
  1. def update_events
  2.     @events.each_value {|event| event.update }
  3.     @common_events.each {|event| event.update }
  4.  
  5.     @usr_events.each {|event| event.update}
  6.   end


然后写一个如下的测试类:


然后我们初始化3个Test类的实例id,i各为0,1,2。然后在一开始的时候加入game_map.usr_events。


然后我们预测一下结果,理想情况下是0,1,2,1,2,2 这样的,一轮删除一个元素。0,1,2第一轮;1,2第二轮;2第三轮的。
实际结果却是如下:

0,2,1,2,1,2这种。

分析原因:
我们可以假设这种迭代器是没有考虑在遍历过程中还会删除某些元素的。那么我估计它会是这样实现的。
RUBY 代码复制
  1. i=0;
  2. while i<usr_events.size
  3. usr_events[i].update
  4. i+=1;
  5. end

那么就会出此类问题。一开始数组是[0,1,2]这样一个结构。i=0的时候遍历0号元素,结果0号元素在执行delete的时候删除了自身导致整个数组结构成了[1,2]。但是代码并没有理会这层结构变化依然将i++;i=1的时候遍历的元素是2号元素,这样就导致了在这次遍历过程中忽略了1号元素。所以在上面那个例子显示0过后不是1而是跳过了1显示2了。

总结:
在遍历数组的时候最好不要删除元素和往数组当中插入元素,这会导致数组遍历混乱,绝对不是你想要的结果。如果向我上面那种在事件中动态添加删除事件,假设总事件是n,那么最坏情况下可能有一个事件会遍历了(1/4)n^2才会得到一次遍历机会。在巨量添加事件的情况下可能会导致巨大的延迟。

Lv1.梦旅人

Mr.Gandum

梦石
0
星屑
226
在线时间
2070 小时
注册时间
2007-1-31
帖子
3039

贵宾

16
发表于 2012-10-10 17:22:51 | 只看该作者
本帖最后由 feizhaodan 于 2012-10-10 18:28 编辑

其实比较安全的方法就是在遍历的时候,将要删除的要素改成nil,遍历后使用compact!方法清除nil要素。
然后这类讨论问题最好发到讨论区,原创技术发布区主要还是用来发布一些脚本或事件范例的囧
回复 支持 反对

使用道具 举报

Lv3.寻梦者

可以吃的头衔

梦石
0
星屑
1010
在线时间
423 小时
注册时间
2010-10-17
帖子
602
15
发表于 2012-10-10 14:20:14 | 只看该作者
qbjxiaolei 发表于 2012-10-10 14:15
怎么用的啊?能不能给个例子我看看哇。

是枚举类的内置方法
a=[b,c,d]
a.delete_if{|obj| obj.id%2==0}

点评

还有select一系列的/A\  发表于 2012-10-10 14:32
……
回复 支持 反对

使用道具 举报

Lv1.梦旅人

梦石
0
星屑
50
在线时间
56 小时
注册时间
2012-10-1
帖子
36
14
 楼主| 发表于 2012-10-10 14:15:28 | 只看该作者
yangff 发表于 2012-10-10 13:20
敢加个clone吗?

怎么用的啊?能不能给个例子我看看哇。
回复 支持 反对

使用道具 举报

Lv3.寻梦者

可以吃的头衔

梦石
0
星屑
1010
在线时间
423 小时
注册时间
2010-10-17
帖子
602
13
发表于 2012-10-10 14:03:22 | 只看该作者
delete_if 。

点评

这个是一个内置方法吗?。。  发表于 2012-10-10 14:16
回复 支持 反对

使用道具 举报

Lv2.观梦者

傻♂逼

梦石
0
星屑
374
在线时间
1606 小时
注册时间
2007-3-13
帖子
6562

烫烫烫开拓者

12
发表于 2012-10-10 13:20:52 | 只看该作者
敢加个clone吗?
回复 支持 反对

使用道具 举报

Lv3.寻梦者

梦石
0
星屑
1641
在线时间
2205 小时
注册时间
2010-6-27
帖子
1299
11
发表于 2012-10-10 11:58:01 | 只看该作者
动态数组遍历时修改数组本身就是不安全的吧 = =
完全可以遍历前后再进行修改

于是我也广告下…
回复 支持 反对

使用道具 举报

Lv1.梦旅人

梦石
0
星屑
50
在线时间
56 小时
注册时间
2012-10-1
帖子
36
10
 楼主| 发表于 2012-10-10 11:05:40 | 只看该作者
的确,删除后面的元素不会出问题应该,但是删除的元素位置大多都是不确定的可是。虽然性能会降低,我还是这样修改吧,遍历中要删除的元素放在一个其他的数组中,等遍历结束后再来删除。

点评

有时候用来做事情的遍历和用来删除的遍历,不一定循环融合或者分开哪个效率高点,这也不见得是性能降低。  发表于 2012-10-10 12:51
回复 支持 反对

使用道具 举报

Lv1.梦旅人

梦石
0
星屑
55
在线时间
1119 小时
注册时间
2012-7-24
帖子
600
9
发表于 2012-10-10 09:51:12 | 只看该作者
从后面删除吧。
a.reverse.each{|e| ...}
怕鼠的猫
回复 支持 反对

使用道具 举报

头像被屏蔽

Lv2.观梦者 (禁止发言)

梦石
0
星屑
653
在线时间
3774 小时
注册时间
2011-2-26
帖子
1839

开拓者

8
发表于 2012-10-10 08:59:52 | 只看该作者
提示: 作者被禁止或删除 内容自动屏蔽
签名被屏蔽
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 注册会员

本版积分规则

拿上你的纸笔,建造一个属于你的梦想世界,加入吧。
 注册会员
找回密码

站长信箱:[email protected]|手机版|小黑屋|无图版|Project1游戏制作

GMT+8, 2025-1-27 14:29

Powered by Discuz! X3.1

© 2001-2013 Comsenz Inc.

快速回复 返回顶部 返回列表