2016-06-11 40 views
1
def unique(arr) 
    return arr.keep_if { |x| arr.count(x) == 1 } 
end 

print unique([2, 5, 5, 4, 22, 8, 2, 8]) 
#=> [4, 22, 2] 

值2在数组中出现两次,但使用以下方法错误地返回它。为什么会发生这种情况,我该如何解决这个问题?为什么在Ruby中使用keep_if会跳过数组中的第一个元素?

+0

如果你是Matz,你将如何实现'Array#keep_if'? – Aetherus

+0

因为在删除第一个'2'后,只剩下一个'2',所以当你遇到第二个'2'时,条件是'true'。可变状态再次发生。 –

+0

@JörgWMittag如果是这样的话,为什么5和8不会返回? –

回答

4

不幸的是,这是由于keep_if工作方式中的一些隐藏行为。为了说明这个问题,我们可以利用挂最低的水果在我们的调试果园,好醇” puts

def unique(arr) 
    return arr.keep_if { |x| 
    puts x, arr.join(',') 
    arr.count(x) == 1 
    } 
end 

print unique([2, 5, 5, 4, 22, 8, 2, 8]) 

这给了我们以下的输出:

2 
2,5,5,4,22,8,2,8 
5 
2,5,5,4,22,8,2,8 
5 
2,5,5,4,22,8,2,8 
4 
2,5,5,4,22,8,2,8 
22 
4,5,5,4,22,8,2,8 
8 
4,22,5,4,22,8,2,8 
2 
4,22,5,4,22,8,2,8 
8 
4,22,2,4,22,8,2,8 
[4, 22, 2] 

仔细看确切地说,当方法发现一个新的值时会发生什么:它将该值存储在数组中的一个早期索引中,覆盖已经存在的内容。下一次它找到一个想要保留的值时,它将它放在下一个点,等等。

这意味着第一次keep_if看起来2,它看到其中两个,因此决定跳过它。但它看到它想要保留的4,并且会覆盖第一个2。因此,第二次看到2时,它决定保留它。

+0

那么为什么5不会保留,因为其中的一个值被22所覆盖? –

+1

,因为直到_after_ 5被考虑之前它并没有被夸大。 – Hamms

+0

@ChristopherByrd我相信这是因为它已经在发生的时间里通过了5秒。 –

1

您在迭代时正在更改数组。这是Matz自己的considered undefined behavior,如果你想避免像你在那里看到的奇怪行为,你应该避免这样做。

而不是你keep_if方法,你应该使用这样的:

arr.select{ |x| arr.count(x) == 1 } 

这不会更改到位数组但却会返回一个新的。

+0

你的第一句话太强大了。 'arr = [1,2,3]; arr.map! {| n | 2 * n}'在你遍历它时改变'arr',但它不是未定义的行为。我们通常知道你的意思 - 从数组中删除元素(或将元素插入到数组中),但你应该更精确。 –

+0

实际上,在这种情况下,当你改变它的时候('count'),你正在迭代它('keep_if');-)这就是为什么这个问题比改变数组这个有点众所周知的问题更加微妙同时迭代。 –

相关问题