为什么Ruby有TrueClass和FalseClass而不是一个布尔类?

当我发现关于这个的时候,我正在研究序列化值。 Ruby有一个TrueClass类和一个FalseClass类,但它没有Boolean类。 我想知道这是为什么。

我看到使用Boolean一些优势; 例如,stringparsing可以集中在它上面。

Ruby开发人员比我聪明,所以一定有很多好的理由,我只是不明白。 但现在看起来像OneClassTwoClass而不是Fixnum

一个类的目的是把类似的对象或者具有类似行为的对象组合在一起。 12是非常相似的,所以他们在同一个class上是完全合理的。 然而false 相似。 事实上,他们的观点是,他们完全相反 ,行为相反。 因此,他们不属于同一个class级。

你能给出一个例子,你会在Boolean类中实现什么样的共同行为? 我想不出什么

让我们看看TrueClassFalseClass的行为:那里只有四个方法。 不再。 在每一个案例中,这两种方法完全相反 。 如何以及为什么要把它放在一个class级?

以下是你如何实现所有这些方法:

 class TrueClass def &(other) other end def |(_) self end def ^(other) !other end def to_s 'true' end end 

现在反过来:

 class FalseClass def &(_) self end def |(other) other end def ^(other) other end def to_s 'false' end end 

当然,在Ruby中,幕后有很多“魔术”,而这些魔术实际上并没有被TrueClassFalseClass处理,而是被硬编入了解释器。 东西, if&&||! 。 然而,在Ruby中借用了很多,包括FalseClassTrueClass的概念的FalseClass ,所有这些都作为方法来实现,你可以在Ruby中做同样的事情:

 class TrueClass def if yield end def ifelse(then_branch=->{}, _=nil) then_branch.() end def unless end def unlesselse(_=nil, else_branch=->{}) ifelse(else_branch, _) end def and yield end def or self end def not false end end 

另一种方式是:

 class FalseClass def if end def ifelse(_=nil, else_branch=->{}) else_branch.() end def unless yield end def unlesselse(unless_branch=->{}, _=nil) ifelse(_, unless_branch) end def and self end def or yield end def not true end end 

几年前,我只是为了好玩而写了上面的文章, 甚至发表了它 。 除了语法看起来不同的事实,因为Ruby使用特殊的操作符,而我只使用方法,它的行为就像Ruby的内置操作符一样。 事实上,我实际上已经将RubySpec一致性testing套件 移植到了我的语法中 ,并且通过了testing。

看来,马茨自己在2004年的一封邮件列表中回答了这个问题。

他的答案简短的版本:“现在它工作正常,添加布尔没有给予任何优势”。

就我个人而言,我不同意; 前面提到的“stringparsing”就是一个例子。 另一个是,当你根据不同的types对variables进行不同的处理时,(例如,一个ymlparsing器)是一个“布尔”类,它会移除一个“if”。 这看起来更正确,但是这是个人意见。

在Ruby论坛上引用Matz (2013) :

…没有什么真实和虚假的共同分享,因此没有布尔类。 除此之外,在Ruby中,一切都performance为布尔值….

true和false可以由一个拥有多个值的布尔类来pipe理,但是这个类对象必须具有内部值,因此必须在每次使用时都去除引用。

相反,Ruby将true和false视为long值(0和1),每个值都对应于一个对象类(FalseClass和TrueClass)types。 通过使用两个类而不是一个布尔类,每个类不需要任何值,因此可以简单地通过它的类标识符(0或1)来区分。 我相信这会转化为Ruby引擎内部的显着速度优势,因为内部Ruby可以将TrueClass和FalseClass视为需要从其ID值进行零转换的long值,而在对Boolean值进行评估之前,必须先取消引用它。

因为在默认情况下,Ruby中的所有内容都是falsenil ,所以只需要将parsing添加到S​​tring。

像这样的东西可以工作:

 class Object ## Makes sure any other object that evaluates to false will work as intended, ## and returns just an actual boolean (like it would in any context that expect a boolean value). def trueish?; !!self; end end class String ## Parses certain strings as true; everything else as false. def trueish? # check if it's a literal "true" string return true if self.strip.downcase == 'true' # check if the string contains a numerical zero [:Integer, :Float, :Rational, :Complex].each do |t| begin converted_number = Kernel.send(t, self) return false if converted_number == 0 rescue ArgumentError # raises if the string could not be converted, in which case we'll continue on end end return false end end 

使用时,这会给你:

 puts false.trueish? # => false puts true.trueish? # => true puts 'false'.trueish? # => false puts 'true'.trueish? # => true puts '0'.trueish? # => false puts '1'.trueish? # => true puts '0.0'.trueish? # => false puts '1.0'.trueish? # => true 

我相信Ruby背后的“重要思想”的一部分就是让你的程序所固有的行为(如布尔parsing),而不是创build一个完全封装的类,它存在于它自己的命名空间世界(例如BooleanParser)中。

在Ruby中,无和假都是错误的,其他的都是真的。 因此,不需要特定的布尔类。

你可以尝试一下 :

 if 5 puts "5 is true" end 

5评估为真

 if nil puts "nil is true" else puts "nil is false" end 

将打印“无是假”

主要原因仅仅是这样一个事实,即实现布尔expression式的速度比使用布尔类更快,更简单,而这意味着转换。

正如Mongus Pong告诉你的,当你写“如果”的时候,你会要求译员对事情进行评估,然后再进行分支。 如果你有布尔类,你必须分支之前事物的评估转换成布尔值(多一步)。

请记住,布尔类中的这种布尔转换将作为Ruby方法提供。 这个方法可以像其他任何Ruby方法一样dynamic地改变,这样开发人员就可以完全搞定事情了(事实上并不那么严重),但显然这不会让解释器按照自己的意愿优化testing。

你知道,它会取代一些完整的方法调用,这是昂贵的Ruby(记住“发送”方法处理)的几个CPU指令操作… … –

正如其他人所说,你可以“修补”ruby。 创build你自己的class级。 这是我想出的东西。 布尔类中的方法有点愚蠢,但是在某些时候它们可以用编程方式实现。

 class Boolean def self.new(bool) bool end def self.true true end def self.false false end end class FalseClass def is_a?(other) other == Boolean || super end def self.===(other) other == Boolean || super end end class TrueClass def is_a?(other) other == Boolean || super end def self.===(other) other == Boolean || super end end