为什么uniq!如果没有重复,则返回nil

时间:2010-01-20 14:59:42

标签: ruby arrays uniq

我刚开始使用Ruby,我个人发现以下内容违反了“最少惊喜原则”。那就是引用the documentation,那就是uniq! “从self中删除重复的元素。如果没有进行任何更改,则返回nil(即没有找到重复项)。”

有人可以解释一下,这对我来说似乎完全违反直觉吗?这意味着,而不是通过附加.uniq来编写下面的一行代码!为了结束第一行,我不得不写下以下两行:

  hooks = IO.read(wt_hooks_impl_file).scan(/wt_rt_00\w{2}/)
  hooks = hooks.uniq

或者我错过了什么,更好的方法?

编辑:

我明白uniq!修改其操作数。以下是我希望更好地说明的问题:

  hooks = IO.read(wt_hooks_impl_file).scan(/wt_rt_00\w{2}/)
  puts hooks.length #50
  puts hooks.uniq!.length #undefined method `length' for nil:NilClass

我认为uniq的方式!作品使它完全无意义和无用。在我的情况下,我指出我可以将.uniq附加到第一行。但是后来在同一个程序中,我将元素推送到循环内的另一个数组中。然后,在循环下,我想“删除”数组,但我不敢写'hooks_tested.uniq!'因为它可以返回零;相反,我必须写hooks_tested = hooks_tested.uniq

事实上,我认为这是一个特别令人震惊的错误特征,因为它是一个众所周知的原则,当设计一个返回数组的方法时,应该总是至少返回一个空数组,而不是nil

5 个答案:

答案 0 :(得分:10)

这是因为uniq!修改了self,如果uniq!会返回一个值,您将无法知道原始对象中是否实际发生了更改。

var = %w(green green yellow)
if var.uniq!
  # the array contained duplicate entries
else
  # nothing changed
end

在您的代码中,您只需编写

即可
hooks = IO.read(wt_hooks_impl_file).scan(/wt_rt_00\w{2}/)
hooks.uniq!
# here hooks is already changed

如果你需要返回hook的值,也许是因为它是最后一个方法语句

def method
  hooks = IO.read(wt_hooks_impl_file).scan(/wt_rt_00\w{2}/)
  hooks.uniq
end

或其他

def method
  hooks = IO.read(wt_hooks_impl_file).scan(/wt_rt_00\w{2}/)
  hooks.uniq!
  hooks
end

答案 1 :(得分:5)

uniq!上的感叹号表示它修改了数组而不是返回一个新数组。你应该这样做:

hooks = IO.read(wt_hooks_impl_file).scan(/wt_rt_00\w{2}/).uniq

或者

hooks = IO.read(wt_hooks_impl_file).scan(/wt_rt_00\w{2}/)
hooks.uniq!
puts hooks.length

答案 2 :(得分:2)

您可以将uniq(末尾没有感叹号)附加到第一行的末尾。

或者,如果您坚持使用uniq!,请使用

(hooks = IO.read(wt_hooks_impl_file).scan(/wt_rt_00\w{2}/)).uniq!

答案 3 :(得分:1)

这不是解决原因的答案,而是解决方法。

由于uniq没有返回nil,我使用uniq并将结果分配给新变量,而不是使用bang版本

original = [1,2,3,4]
new = original.uniq

#=> new is [1,2,3,4]
#=> ... rather than nil

拥有一个新变量是一个很小的代价。如果进行检查,重复调用uniq!uniq并检查nil

,那肯定会好坏

答案 4 :(得分:1)

从Ruby 1.9开始,Object#tap可用:

hooks = IO.read(wt_hooks_impl_file).scan(/wt_rt_00\w{2}/).tap do |hooks|
  hooks.uniq!
end
puts hooks.length

也许更简洁(h / t @Aetherus):

hooks = IO.read(wt_hooks_impl_file).scan(/wt_rt_00\w{2}/).tap(&:uniq!)
puts hooks.length