【问题标题】:Why does uniq! return nil if there are no duplicates为什么 uniq!如果没有重复则返回 nil
【发布时间】:2010-01-20 14:59:41
【问题描述】:

我刚开始使用 Ruby,我个人认为以下内容违反了“最小意外原则”。也就是说,引用the documentation,那个uniq! “从自身中删除重复元素。如果没有进行任何更改(即没有找到重复项),则返回 nil。”

谁能解释一下,这对我来说似乎完全违反直觉?这意味着,而不是能够通过附加 .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_tested.uniq!'因为它可以返回 nil;相反,我必须写 hooks_tested = hooks_tested.uniq

确实,我认为这是一个特别令人震惊的错误特性,因为众所周知的原则是,在设计返回数组的方法时,应该始终至少返回一个空数组,而不是 nil

【问题讨论】:

  • POLS 对 Ruby 有特定的含义:“Matz(Ruby 的创造者)并不意外。
  • 对您试图提出的(不正确的)目的无用,同意。如果您尝试做一些事情——例如——测试uniq! 的结果,这会很方便。这可能就是马茨的想法。 ;-)
  • 我同意。这是非常 PHP 式的(随机的、不协调的例外情况)。所有其他情况,包括uniq 返回原始情况。当uniq 没有找到重复项时,它会返回数组本身。严重的wtf。
  • @MikeWoodhouse 用于该目的的方法应该是 uniq? 而不是 uniq!

标签: ruby arrays uniq


【解决方案1】:

这是因为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

如果你需要返回钩子的值,可能是因为它是最后一个方法语句,就这样做

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

【讨论】:

  • “exclamation”方法的返回值通常是任意的,因为它们就地修改了对象。这种情况的不幸之处在于它造成的一些语义混乱,如您的示例所示: if uniq!那么……其实并不是独一无二的。
【解决方案2】:

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

【讨论】:

  • 通常,如果您使用方法的 bang 形式,它将是唯一作用于该特定对象的方法(即,不在链中)。这部分是为了消除模棱两可的语句,例如hooks = hooks.uniq!.sort 或hooks.uniq!.sort!(您可以在同一表达式中对同一变量进行多次赋值)或对中间临时值的赋值,例如hooks.uniq.sort!。 Array.uniq! 还返回 nil 而不是 [],因此您可以执行 if (hooks.uniq!) 之类的操作来修改数组并在发生任何更改时执行特殊操作。
  • 如果挂钩不包含重复项,您的第二个建议会导致“nil:NilClass 的未定义方法‘长度’”——这正是我试图引起注意的意外行为,但显然它落在不合逻辑的——呃,聋了——耳朵
  • 好的,抱歉。如果第二个示例不起作用,那么这里还有其他事情发生。您使用的是哪个版本的 Ruby?
【解决方案3】:

自 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

【讨论】:

    【解决方案4】:

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

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

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

    【讨论】:

    • 是的,我想我可以在这种情况下
    【解决方案5】:

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

    由于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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2012-11-21
      • 1970-01-01
      • 1970-01-01
      • 2019-02-28
      • 2016-02-28
      • 1970-01-01
      • 2013-05-09
      • 1970-01-01
      相关资源
      最近更新 更多