【问题标题】:Best ruby idiom for "nil or zero"“零或零”的最佳红宝石成语
【发布时间】:2023-03-30 13:38:01
【问题描述】:

我正在寻找一种简洁的方法来检查一个值,看看它是零还是零。目前我正在做类似的事情:

if (!val || val == 0)
  # Is nil or zero
end

但这看起来很笨拙。

【问题讨论】:

  • 如果 val 等于 false 会发生什么?

标签: ruby design-patterns idioms null


【解决方案1】:

我认为您的代码不正确;它实际上会测试三个值:nilfalse 和零。这是因为 !val 表达式对于所有为假的值都为真,在 Ruby 中为 nilfalse

我现在能想到的最好的是

if val == nil || val == 0
  # do stuff
end

这当然不是很聪明,但(非常)清楚。

【讨论】:

  • 您可能会假设有问题的变量是一个数字。如果您甚至不知道它是布尔值还是数字,代码中就会出现更大的问题。
【解决方案2】:

首先,我认为这是检查特定条件的最简洁方法。

其次,对我来说,这是一种代码异味,表明您的设计存在潜在缺陷。一般来说,零和零不应该意味着同样的事情。如果可能的话,您应该在点击此代码之前尝试消除 val 为 nil 的可能性,方法是在方法的开头检查它或其他一些机制。

您可能有完全正当的理由这样做,在这种情况下,我认为您的代码很好,但我至少会考虑在可能的情况下尝试摆脱 nil 检查。

【讨论】:

【解决方案3】:

你可以使用 Object.nil 吗?专门测试 nil(而不是陷入 false 和 nil 之间)。您也可以将方法修补到 Object 中。

class Object
   def nil_or_zero?
     return (self.nil? or self == 0)
   end
end

my_object = MyClass.new
my_object.nil_or_zero?
==> false

不建议这样做,因为对 Object 的更改很难让同事跟踪,并且可能会使您的代码对其他人无法预测。

【讨论】:

  • 我会修改 Numeric 类而不是 Object。
  • epochwolf,如果你把它放在 Numeric 类上,那么 nil? 将始终返回 false。 nil? 仅在 NilClass 上返回 true。
  • 您可以实现ObjectNilClassNumeric中的方法。 Object 将返回 falseNilClass 将返回 trueNumeric 将返回 zero?
【解决方案4】:

Rails 通过属性查询方法做到这一点,其中除了 false 和 nil 之外,0 和 "" 也评估为 false。

if (model.attribute?) # => false if attribute is 0 and model is an ActiveRecord::Base derivation

不过,它也有不少批评者。 http://www.joegrossberg.com/archives/002995.html

【讨论】:

  • 链接无效。我对#attribute的方法很好奇,因为我找不到类似的东西。
  • 这是 Rails/ActiveRecord 动态添加的。如果客户有一个名称字段,名称?自动添加以检查名称是空白还是 nil。请参阅api.rubyonrails.org/classes/ActiveRecord/Base.html - 搜索“属性查询方法”部分
【解决方案5】:

对象有一个nil? method

if val.nil? || val == 0
  [do something]
end

或者,只需一条指令:

[do something] if val.nil? || val == 0

【讨论】:

  • 注意“or”和“and”的使用,顺便说一句,与 || 相比,它们具有不同且非常低的优先级和 &&。一些评论请参见blog.jayfields.com/2007/08/…
  • 我同意问题中“气味”的一些海报,但对我来说这更像是红宝石方式。
  • or 的优先级在这里不需要任何括号,对于有经验的 Rubyist 来说,它会完全自然地阅读。唯一被||or 咬过的人是从未编写过Perl 的人:)
  • 你唯一需要担心的时候||与 OR 优先级是在分配中,而不是布尔检查。但是,单词的低优先级对于错误检查非常方便。
  • 我觉得val && val == 0更好。
【解决方案6】:

我通过定义一个“是?”来处理这个问题。方法,然后我可以在各种类上以不同的方式实现它。所以对于 Array,“是?”表示“大小>0”;对于 Fixnum,它的意思是“自我!= 0”;对于字符串,它的意思是“自我!=''”。 NilClass 当然定义了“是?”就像返回 nil 一样。

【讨论】:

    【解决方案7】:

    为了尽可能地道,我建议这样做。

    if val.nil? or val == 0
        # Do something
    end
    

    因为:

    • 它使用零?方法。
    • 它使用“或”运算符,比 || 更可取。
    • 它不使用括号,在这种情况下这不是必需的。仅当括号用于某些目的时才应使用括号,例如覆盖某些运算符的优先级。

    【讨论】:

    【解决方案8】:
    val ||= 0
    if val == 0
    # do something here
    end
    

    【讨论】:

    • -1:如果val中的值需要保留怎么办?您正在使用这种方法破坏输入数据,并且有更好、破坏性更小的方法来检查 nil 或零。
    • if (val || 0).zero 怎么样?
    【解决方案9】:

    如果你真的很喜欢末尾带有问号的方法名:

    if val.nil? || val.zero? # do stuff end

    您的解决方案很好,其他一些解决方案也很好。

    如果你不小心的话,Ruby 可以让你寻找一种漂亮的方式来做任何事情。

    【讨论】:

      【解决方案10】:

      如果你愿意,可以使用case

       case val with nil, 0
            # do stuff
       end
      

      然后你可以使用任何与=== 一起工作的东西,这有时很好。或者做这样的事情:

      not_valid = nil, 0
      case val1 with *not_valid
            # do stuff
       end
       #do other stuff
       case val2 with *not_valid, false    #Test for values that is nil, 0 or false
            # do other other stuff
       end
      

      这不是完全好的 OOP,但它非常灵活并且有效。我的ifs 通常以cases 结尾。

      当然Enum.any?/Enum.include? 也可以...如果你想变得非常神秘:

      if [0, nil].include? val
          #do stuff
      end
      

      正确的做法当然是定义一个方法或函数。或者,如果您必须对许多值执行相同的操作,请使用这些不错的迭代器的组合。

      【讨论】:

        【解决方案11】:

        另一种解决方案:

        if val.to_i == 0
          # do stuff
        end
        

        【讨论】:

        • 这仅在 val 为整数或 nil 时有效; 0.5.to_i == 0,any_string.to_i == 0 等
        • "5".to_i == 5,但你或多或少是对的。聪明但实际上并不奏效。
        【解决方案12】:

        nil.to_i 返回零,所以我经常这样做:

        val.to_i.zero?
        

        但是,如果 val 曾经是一个不响应 #to_i 的对象,则会出现异常。

        【讨论】:

        • 供参考:Scott's answer 以及@AndrewGrimm 对此的评价:"5".to_i == 5, but you're more or less right. Clever but doesn't actually work
        【解决方案13】:

        我真的很喜欢 Rails 的 blank? 方法来处理这类事情,但它不会为 0 返回 true。所以你可以添加你的方法:

        def nil_zero? 
          if respond_to?(:zero?) 
            zero? 
          else 
            !self 
          end 
        end 
        

        它会检查某个值是 nil 还是 0:

        nil.nil_zero?
        => true
        0.nil_zero?
        => true
        10.nil_zero?
        => false
        
        if val.nil_zero?
          #...
        end
        

        【讨论】:

          【解决方案14】:

          这很简洁:

          if (val || 0) == 0
            # Is nil, false, or zero.
          end
          

          只要您不介意将false 视为与nil 相同,它就可以工作。在我从事的项目中,这种区别只在一段时间内很重要。其余时间我个人更喜欢跳过.nil?,代码略短。

          [更新:我不再写这种东西了。它有效,但太神秘了。我试图通过改变我做过的几个地方来纠正我的错误行为。]

          顺便说一句,我没有使用.zero?,因为如果val 是一个字符串,这会引发异常。但是.zero? 会很好,如果您知道情况并非如此。

          【讨论】:

          • [edited] 我认为如果val 为nil,那么这个表达式将计算为nil == 0(因此返回false)。然而它看起来确实有效,虽然我认为它不是很可读。
          • 我现在认为我这里的方法太神秘了,只是为了节省几个字符,我现在不会这样写。所以我不期待任何赞成票,但我对一个有效答案的反对票感到有点惊讶(尽管它的品味令人怀疑)......
          【解决方案15】:

          从 Ruby 2.1 开始,you could use refinements 不再是猴子修补类。细化类似于猴子补丁;在那里,它们允许您修改类,但修改仅限于您希望使用它的范围。

          如果您想执行此检查一次,这有点过头了,但如果您重复自己,这是猴子修补的一个很好的替代方案。

          module NilOrZero
            refine Object do
              def nil_or_zero?
                nil? or zero?
              end
            end
          end
          
          using NilOrZero
          class Car
            def initialize(speed: 100)
              puts speed.nil_or_zero?
            end
          end
          
          car = Car.new              # false
          car = Car.new(speed: nil)  # true
          car = Car.new(speed: 0)    # true
          

          Refinements were changed 在最后一分钟被限定为文件。所以之前的例子可能已经证明了这一点,但这是行不通的。

          class Car
            using NilOrZero
          end
          

          【讨论】:

            【解决方案16】:

            从 Ruby 2.3.0 开始,您可以将安全导航运算符 (&.) 与 Numeric#nonzero? 结合使用。 &. 返回 nil 如果实例是 nilnonzero? - 如果数字是 0:

            unless val&.nonzero?
              # Is nil or zero
            end
            

            或后缀:

            do_something unless val&.nonzero?
            

            【讨论】:

            • 这是一个了不起的新现代答案。此外,还有一篇很棒的文章介绍了为什么在这里使用它:The Safe Navigation Operator (&.) in Ruby
            • 非常简洁,但如果没有评论,我需要一点时间才能理解 unless val&.nonzero? 转换为“如果 val 为零或零”
            • @Stefan,同意了。我自己不会在生产代码中使用它。我把它包括在内是为了完成主义。反面是完全可读的——do_something if val&.nonzero?(也就是如果val不是nil而不是零)。
            【解决方案17】:

            简洁明了

            [0, nil].include?(val)

            【讨论】:

            【解决方案18】:

            最短最好的方法应该是

            if val&.>(0)
              # do something
            end
            

            对于val&.>(0) 它在 val 为 nil 时返回 nil,因为 > 基本上也是一个方法,在 ruby​​ 中 nil 等于 false。 val == 0 时返回 false。

            【讨论】:

            • 最短的比什么?,最好的方法是什么?
            【解决方案19】:

            对于 nil 和 0,计算结果为 true:nil.to_s.to_d == 0

            【讨论】:

              【解决方案20】:

              我的解决方案也使用细化,减去条件。

              module Nothingness
                refine Numeric do
                  alias_method :nothing?, :zero?
                end
              
                refine NilClass do
                  alias_method :nothing?, :nil?
                end
              end
              
              using Nothingness
              
              if val.nothing?
                # Do something
              end
              

              【讨论】:

                【解决方案21】:
                unless (val || 0).zero?
                
                    # do stufff
                
                end
                

                【讨论】:

                  【解决方案22】:

                  您可以一次性完成:

                  [do_something] if val.to_i == 0
                  

                  nil.to_i 将返回 0

                  【讨论】:

                    猜你喜欢
                    • 2012-09-23
                    • 1970-01-01
                    • 2012-07-07
                    • 1970-01-01
                    • 1970-01-01
                    • 1970-01-01
                    • 1970-01-01
                    • 2012-11-15
                    • 1970-01-01
                    相关资源
                    最近更新 更多