【问题标题】:ruby unless bug with multiple conditionsruby 除非有多个条件的错误
【发布时间】:2017-05-02 23:08:37
【问题描述】:

通用代码:

class ThingA end
class ThingB end
class ThingC end

为了为上述类型设置条件检查,我使用了基本的“if !...”构造,它产生了预期的准确结果。

示例代码如果! ...:

obj = ThingA.new
puts 'yes it is a ThingC' if !obj.is_a?(ThingA) && !obj.is_a?(ThingB) # works ok
# stdout => nothing

obj = ThingB.new
puts 'yes it is a ThingC' if !obj.is_a?(ThingA) && !obj.is_a?(ThingB) # works ok
# stdout => nothing

obj = ThingC.new
puts 'yes it is a ThingC' if !obj.is_a?(ThingA) && !obj.is_a?(ThingB) # works ok
# stdout => yes it is a ThingC

考虑到“除非”是基本“如果!...”构造的更具描述性的替代品这一事实,我使用“除非”来实现上述内容。

示例代码,除非:

obj = ThingA.new
puts 'yes it is a ThingC' unless obj.is_a?(ThingA) && obj.is_a?(ThingB) # BUG
# stdout => yes it is a ThingC

obj = ThingB.new
puts 'yes it is a ThingC' unless obj.is_a?(ThingA) && obj.is_a?(ThingB) # BUG
# stdout => yes it is a ThingC

obj = ThingC.new
puts 'yes it is a ThingC' unless obj.is_a?(ThingA) && obj.is_a?(ThingB) # ???
# stdout => yes it is a ThingC

显然,“除非”版本无法产生完全相同的准确结果。

基于这些简单直接的结果,是否有人很难得出“除非准确,否则无法处理多个条件”的结论?

【问题讨论】:

    标签: ruby multiple-conditions


    【解决方案1】:

    除非有逻辑

    使用unless,布尔逻辑变得难以解析。你可以用if重写它:

    • unless a || b 等价于if !(a || b),即等价于if !a && !b
    • unless a && b 等价于if !(a && b),即等价于if !a || !b

    你的“错误”

    如果ThingAThingB 是独立的类,obj.is_a?(ThingA) && obj.is_a?(ThingB) 必须是false

    如果一个类是另一个类的子类,它只能是true。在这种情况下,您只需要检查 obj 是否是子类实例:

    obj.is_a?(ThingA) && obj.is_a?(Object) 为真当且仅当objThingA 对象,所以你可以写:

    obj.is_a?(ThingA)

    替代方案

    对于您的示例,您可能应该使用 case

    obj = ThingA.new
    
    case obj
    when ThingA
      puts "yes, it is a ThingA"
    when ThingB
      puts "yes, it is a ThingB"
    when ThingC
      puts "yes, it is a ThingC"
    else
      puts "no, it is some other Object"
    end
    

    case obj
    when ThingA, ThingB, ThingC
      puts "yes, it is a Thing A B or C"
    else
      puts "no, it is some other Object"
    end
    

    puts "yes, it is a #{obj.class}"
    

    他们回来了:

    yes, it is a ThingA
    yes, it is a Thing A B or C
    yes, it is a ThingA
    

    【讨论】:

    • 如果您从“unless expr 等同于 if !(expr)”开始,然后继续使用复合 expr 和德摩根定律,可能会更清楚。
    【解决方案2】:

    这里的问题在于逻辑和或或,当你否定一个逻辑函数时,等价的是运算符的相反和优先级:修饰符之前的逻辑函数

    德摩根定律提供了一种分配否定的方式 析取和合取:

    ¬ ( a ∨ b ) ≡ ( ¬ a ∧ ¬ b ), 
    

    和 ¬ ( a ∧ b ) ≡ ( ¬ a ∨ ¬ b )

    在你的情况下,

    (not(a) and not(b)) 然后你否定它 => not(a or b)

    并且除非 => 不是如果

    irb(main):001:0> class ThingA end
    class ThingB end
    class ThingC end
    => nil
    irb(main):002:0> => nil
    irb(main):003:0> => nil
    
    irb(main):004:0> obj = ThingA.new
    puts 'yes it is a ThingC' unless obj.is_a?(ThingA) || obj.is_a?(ThingB
    
    obj = ThingB.new
    puts 'yes it is a ThingC' unless obj.is_a?(ThingA) || obj.is_a?(ThingB) 
    
    obj = ThingC.new
    puts 'yes it is a ThingC' unless obj.is_a?(ThingA) || obj.is_a?(ThingB) 
    
    <ThingA:0x000000025fc060>
        irb(main):005:0> => nil
    <ThingB:0x00000002749d00>
        irb(main):009:0> => nil
    <ThingC:0x000000027be420>
        irb(main):013:0> yes it is a ThingC
        => nil
    

    【讨论】:

      【解决方案3】:

      我在这里没有看到任何错误。你得到的输出是合乎逻辑的。让我分解你的代码。

      obj = ThingA.new
      puts 'yes it is a ThingC' unless obj.is_a?(ThingA) && obj.is_a?(ThingB) # BUG
      

      obj.is_a?(ThingB)false。所以false &amp;&amp; obj.is_a?(ThingB)falseunless falsetrue。所以它会打印出来。

      obj = ThingB.new
      puts 'yes it is a ThingC' unless obj.is_a?(ThingA) && obj.is_a?(ThingB) 
      

      obj.is_a?(ThingA) 是假的。 false &amp;&amp; obj.is_a?(ThingB)falseunless falsetrue。所以它会打印出来。

      obj = ThingC.new
      puts 'yes it is a ThingC' unless obj.is_a?(ThingA) && obj.is_a?(ThingB)
      

      obj.is_a?(ThingA)falsefalse &amp;&amp; obj.is_a?(ThingB)false.unless falsetrue。所以它会打印出来。

      【讨论】:

        【解决方案4】:

        注意:由于我想发布的文字无法作为评论,因此我只能将其作为答案分享。

        感谢大家提供的所有宝贵答案和 cmets。现在,正如我已经提到的,我想设置一个“Neither x AND Nor y”过滤器。我去了if !obj.is_a?(ThingA) &amp;&amp; !obj.is_a?(ThingB)。它不仅有效,而且还清楚地传达了“既不是 ThingA 也不是 ThingB”的意图。

        现在问题来了,为什么我要从这个语句中删除所有的!s,用unless 替换if 并期望新版本产生相同的结果?答案在于我们中的一些人/大多数人是如何被介绍给unless的。像“Ruby 也可以执行除非测试,这与 if 测试完全相反......”和“认为除非是表达“如果不是”的另一种方式。”(直接引用 Huw 的 The Book of Ruby Collingbourne:pg87),基本上导致(更像是误导)我相信unless 可以在任何情况下用作if ! 的替代品。这就是为什么我希望它也能在这里工作。

        新年决议项目 1:unless 严格禁止涉及多个条件的任何事情。

        任何仍然一心想解决unless 的复合条件if ! 问题的人都可以通过将整个论点包含在!() 中来做到这一点。例如,上面可以解决为unless !( !obj.is_a?(ThingA) &amp;&amp; !obj.is_a?(ThingB) )。仅以学术精神分享。不要因为投反对票而气馁。

        【讨论】:

          猜你喜欢
          • 2014-09-24
          • 2020-11-28
          • 1970-01-01
          • 2020-06-16
          • 2019-01-02
          • 2013-05-16
          • 1970-01-01
          • 1970-01-01
          • 2013-08-19
          相关资源
          最近更新 更多