【问题标题】:ruby syntactic sugar: dealing with nilsruby 语法糖:处理 nil
【发布时间】:2010-12-30 19:15:43
【问题描述】:

可能已经问过了,但我找不到它..这里有 2 种常见情况(对我来说,在编写 rails 时..)用 ruby​​ 编写令人沮丧:

"a string".match(/abc(.+)abc/)[1]

在这种情况下,我得到一个错误,因为字符串不匹配,因此 [] 运算符在 nil 上调用。我想找到以下更好的替代方法:

temp="a string".match(/abc(.+)abc/); temp.nil? ? nil : temp[1]

简而言之,如果不匹配,则返回 nil 而不会出错

第二种情况是这样的:

var = something.very.long.and.tedious.to.write
var = something.other if var.nil?

在这种情况下,我只想将一些东西分配给 var,如果它不是 nil,如果它是 nil,我会分配 something.other..

有什么建议吗? 谢谢!

【问题讨论】:

  • 好吧,我很累,我很抱歉第二个问题很愚蠢(是的,这是一个简单的 OR..),但第一个问题仍然很有趣..
  • 其实第二个也很有趣。我暂时弄错了,因为我忘记了 or 运算符的优先级。滥用or 是一种很常见的失礼行为,值得强调。
  • 这与这个问题非常相似:stackoverflow.com/questions/4371716/…

标签: ruby idioms


【解决方案1】:

在 Ruby on Rails 中,您可以在任何对象上使用 try 方法。根据 API:

调用由符号方法标识的方法,将任何参数和/或指定的块传递给它,就像常规的 Ruby Object#send 所做的那样。

但是,与该方法不同的是,如果接收对象是 nil 对象或 NilClass,则不会引发 NoMethodError 异常,而是返回 nil。

所以对于第一个问题,你可以这样做:

"a string".match(/abc(.+)abc/).try(:[], 1)

它会给你 [1] 或 nil 而不会出错。

【讨论】:

  • 此代码难以阅读,只有在 ActiveSupport 存在时才有效。
  • 这个问题在你修改之前最初是用 ruby​​-on-rails 标记的,所以我提出了 Ruby on Rails 中内置的最干净的解决方案。并且难以阅读?大多数 Ruby 程序员都习惯了“send”方法的工作原理,并且使用外部 Ick 库,这几乎不比接受的答案低可读性。您也不妨对此投反对票,因为它“仅在 Ick 存在时才有效”。总的来说,这是毫无根据的反对票。
  • 考虑到 tokland 的答案的后半部分是 100% 正确的,那 是毫无根据的反对票。虽然最初的问题被标记为 ruby​​-on-rails,但该问题与 rails 没有任何关系。不必要的依赖是 Ruby 程序员最讨厌的错误之一。但是在您的情况下,您不仅添加了不必要的依赖项。您的实现还引入了不必要且缓慢的发送调度,并且如前所述,它很难阅读。我永远不会让这样的代码通过代码审查来实现。所以是的,投反对票。
【解决方案2】:

忘记 Python 返祖吧!

"a string"[/abc(.+)abc/,1] # => nil

【讨论】:

  • +1 用于在与编程相关的上下文中使用“返祖”一词。
  • 我知道match的唯一目的是stackoverflow.com/questions/4472848/…
  • 其实,前段时间我写了一个路由机制,在match/captures方法对上进行duck-typed。这让我可以轻松地将 URI 模板匹配引入路由系统。实际上,我发现以不那么惯用的方式执行正则表达式有很多好处。它们往往是您在元编程或创建 DSL 时编写的内容。
【解决方案3】:
"a string"[/abc(.+)abc/, 1]
# => nil
"abc123abc"[/abc(.+)abc/, 1]
# => "123"

还有:

var = something.very.long.and.tedious.to.write || something.other

请注意or 的运算符优先级与|| 不同,对于这种用法应该首选||or 运算符用于流控制,例如ARGV[0] or abort('Missing parameter')

【讨论】:

    【解决方案4】:

    首先我推荐ickmaybe(相当于andand

    "a string".match(/abc(.+)abc/).maybe[1]
    

    我不确定我是否理解第二个,你想要这个吗?

    var = something.very.long.and.tedious.to.write || something.other
    

    【讨论】:

      【解决方案5】:
      "a string".match(/foo(bar)/).to_a[1]
      

      NilClass#to_a 返回一个空数组,在它之外进行索引会为您提供nil 值。

      或者(我做的)你可以把火柴扔掉:

      _, some, more = "a string".match(/foo(bar)(jim)/).to_a
      

      【讨论】:

      • @BobAman 您愿意详细说明一下吗?你反对哪一部分?在您只关心子匹配的情况下,使用下划线变量默默地忽略完整匹配?它不会帮助任何人投反对票,说“Ewww”,然后走开。
      • 我指的是使用NilClass#to_a 方法加上超出范围的索引。像鸭子打字一样有趣,你在这里太聪明了。聪明是坏事。无论如何,打火柴更具可读性。但是,如果我一次分配多个捕获组,我只会这样做。否则,您应该使用 String#[] 方法,因为这就是它的用途。
      • @BobAman 我同意String#[] 在提取单个匹配项时更好。我只在多次捕获时执行上述操作。我也同意,为了聪明而聪明是不好的。然而,IMO 这并不聪明。我觉得这是我在多个地方看到的常见成语;我第一次看到它是由 Ara T. Howard 使用和提倡的。感谢您的澄清。
      【解决方案6】:

      对于第一个问题,我觉得 Bob 的回答很好。

      对于第二个问题,

      var = something.very.long.and.tedious.to.write.instance_eval{nil? ? something.other : self}
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2020-07-24
        • 2012-06-28
        • 2010-10-31
        • 2013-07-05
        • 2011-04-21
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多