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

Ruby 为什么uniq!如果没有重复项,则返回nil,ruby,arrays,uniq,Ruby,Arrays,Uniq,我只是从Ruby开始,我个人发现下面的内容违反了“最小惊喜原则”。也就是说,引用那个uniq!从self中删除重复的元素。如果未进行任何更改(即未找到重复项),则返回nil 有人能解释这一点吗?我觉得这完全违反直觉?这意味着,与其通过添加.uniq来编写下面的一行代码,还不如添加.uniq!为了结束第一行,我必须写以下两行: hooks = IO.read(wt_hooks_impl_file).scan(/wt_rt_00\w{2}/) hooks = hooks.uniq 还是我错

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

有人能解释这一点吗?我觉得这完全违反直觉?这意味着,与其通过添加.uniq来编写下面的一行代码,还不如添加.uniq!为了结束第一行,我必须写以下两行:

  hooks = IO.read(wt_hooks_impl_file).scan(/wt_rt_00\w{2}/)
  hooks = hooks.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\u tested.uniq!”因为它可以返回零;相反,我必须编写hooks\u tested=hooks\u tested.uniq


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

您可以在第一行末尾附加
uniq
(末尾没有感叹号)

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

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

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

这是因为
uniq修改
self
和if
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

这不是为什么的答案,而是一个解决办法

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

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

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

拥有一个新的变量是一个很小的代价。这当然比做if检查要好得多,重复调用
uniq
uniq
并检查
nil

自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):


是的,我想我可以在这种情况下,POLS对Ruby有一个特定的含义:“不会让Matz(Ruby的创建者)感到惊讶。对(不正确的)用户没有用。”您试图表达的目的,同意。如果您试图做一些事情,例如测试
uniq!
,这可能就是Matz的想法。;-,我同意。这是非常PHP风格的(随机的、不一致的例外情况)。所有其他情况,包括
uniq
返回原始情况。当
uniq
未找到重复项时,它将返回数组本身。严重的是wtf。@MikeWoodhouse用于此目的的方法应为
uniq?
而不是
uniq!
来自“感叹号”的返回值“像这样的方法通常是任意的,因为它们在适当的位置修改对象。这个例子的不幸之处在于它造成了一些语义混乱,如您的示例所示:if uniq!然后…实际上不是唯一的。通常,如果你使用方法的bang形式,它将是作用于特定对象的唯一方法(即,不在链中)。这部分是为了消除不明确的语句,例如
hooks=hooks.uniq!。排序
hooks.uniq!。排序(在这里,您可以对同一表达式中的同一变量进行多个赋值)或对中间临时值进行赋值,例如
hooks.uniq.sort<代码>数组.uniq
还返回
nil
而不是
[]
,因此您可以执行
if(hooks.uniq!)
之类的操作来修改数组,并在有任何更改时执行特殊操作。您的第二个建议会导致“nil:NilClass的未定义方法`length'”如果它不包含重复项,这正是我试图引起注意的意外行为,但显然它落在了不合逻辑的——呃,聋子——earsOkay,对不起。如果第二个例子不起作用,这里还发生了其他事情。你使用的是什么版本的Ruby?
hooks = IO.read(wt_hooks_impl_file).scan(/wt_rt_00\w{2}/).tap(&:uniq!)
puts hooks.length