ruby除非有多种条件的bug

时间:2016-12-17 07:40:38

标签: ruby multiple-conditions

通用代码:

class ThingA end
class ThingB end
class ThingC end

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

示例代码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

考虑到“除非”是基本“if!...”构造的更具描述性的替代,我使用“除非”来实现上述。

示例代码,除非:

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

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

基于这些简单而直截了当的结果,任何人都难以得出结论“除非准确地处理多个条件”吗?

4 个答案:

答案 0 :(得分:7)

逻辑除非

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

  • unless a || b相当于if !(a || b),相当于if !a && !b
  • unless a && b相当于if !(a && b),相当于if !a || !b

你的" Bug"

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

ThingA必须为ThingB

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

当且仅当obj.is_a?(ThingA) && obj.is_a?(Object)obj对象时,

ThingA才为真,所以你可以写一下:

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

答案 1 :(得分:2)

这里的问题是逻辑和或或,当你否定一个逻辑函数时,等效的是opossite和操作符的优先级:修饰符之前的逻辑函数

  

德摩根的法律提供了一种分配否定的方式   分离和结合:

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

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

在你的情况下,

  

(不是(a)而不是(b))然后你否定它=>不是(a或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

答案 2 :(得分:0)

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

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 && 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 && 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 && obj.is_a?(ThingB)falseunless falsetrue。所以它打印出来。

答案 3 :(得分:0)

注意:由于我想发布的文字不适合作为评论,我不得不将其作为答案分享。

谢谢大家的所有宝贵答案和评论。现在,正如我已经提到过的那样,我想建立一个&#34;无论是x AND Nor y&#34;过滤。我去了if !obj.is_a?(ThingA) && !obj.is_a?(ThingB)。它不仅有效,而且还清楚地表达了意图&#34; ThingA和Nor ThingB&#34;。

现在问题出现了,为什么我从此语句中删除了所有!,将if替换为unless,并期望新版本产生相同的结果?答案在于我们中的一些/大多数人首先被介绍到unless。类似于&#34; Ruby的语句也可以执行,除非测试与if测试完全相反......&#34;并且&#34;想想除非作为表达“如果不是”的替代方式。&#34; (来自Huw Collingbourne的The Book of Ruby的直接引用:pg87),基本上导致(更像是误导)我相信unless可以在任何场景中用作if !的替代。这就是我期望它在这里工作的原因。

新年的决议第1项:unless严格禁止任何涉及多个条件的内容。

任何仍然坚持用if !解决复合条件unless问题的人都可以通过将整个论点包含在!()中来实现。例如,上述内容可以解决为unless !( !obj.is_a?(ThingA) && !obj.is_a?(ThingB) )。仅在学术精神上分享。不要沮丧地投票。