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

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

我看到了使用 Boolean的一些优点; 例如,字符串解析可以集中在它上面。

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

14472 次浏览

在 Ruby 中,nil 和 false 都是 false,其他的都是 true。因此不需要特定的布尔类。

你可以试试:

if 5
puts "5 is true"
end

5的值为 true

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

将打印“ nil is false”

True 和 false 可以由一个包含多个值的 Boolean 类来管理,但是那样的话类对象必须有内部值,因此必须在每次使用时取消引用。

相反,Ruby 将 true 和 false 视为长值(0和1) ,每个值对应于一种类型的对象类(FalseClass 和 TrueClass)。通过使用两个类而不是一个布尔类,每个类不需要任何值,因此可以通过其类标识符(0或1)进行区分。我相信这转化为 Ruby 引擎内部的速度优势,因为 Ruby 内部可以将 TrueClass 和 FalseClass 视为长值,不需要从它们的 ID 值转换,而 Boolean 对象在被计算之前必须被去引用。

主要原因很简单,实现布尔表达式要比使用布尔类实现转换快得多,也简单得多。

正如 Mongus Pong 告诉你的,当你写“ if”时,你要求解释器评估 东西,然后分支。如果使用布尔类,则必须将 东西的求值转换为布尔 before分支(再执行一个步骤)。

请记住,这样的-> Boolean 转换可以在 Boolean 类中作为 Ruby 方法使用。这个方法可以像任何其他 Ruby 方法一样动态地改变,允许开发人员把事情搞得一团糟(其实没那么严重) ,但是很明显,这不允许解释器按照他们应该的方式优化测试。

你是否意识到它会用一个完整的方法调用来替代一些 CPU 指令操作,而这在 Ruby 中是很昂贵的(还记得“ send”方法处理) ..。

类的目的是将相似的对象或具有相似行为的对象分组在一起。12非常相似,因此它们在同一个班级是完全有意义的。然而,truefalse没有相似的。事实上,他们的 就是这个意思是他们正好是彼此的 相反和有相反的行为。因此,他们不属于同一个班级。

您能举个例子说明在 Boolean类中实现什么样的常见行为吗?我什么都想不起来。

让我们看看 TrueClassFalseClass的行为: 这里确实有 方法。不会再有了。在每一种情况下,这两种方法都执行 恰恰相反。你为什么要把它放在一个单独的课堂里?

下面是实现所有这些方法的方法:

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&&||!。然而,在 Smalltalk 中,Ruby 借鉴了很多东西,包括 FalseClassTrueClass的概念,所有这些都是以方法的形式实现的,你可以在 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 一致性测试套件把它移植到我的语法上,它通过了。

看起来马茨自己在2004年用 mailing list message回答了这个问题。

他的回答很简短: “现在还行,添加一个布尔值并不会带来任何好处。”。

就个人而言,我不同意这种说法; 前面提到的“字符串解析”就是一个例子。另一个问题是,当您根据变量的类型(例如 yml 解析器)对其应用不同的处理时,使用“ Boolean”类是很方便的——它删除了一个“ if”。这看起来也更正确,但这只是个人观点。

因为在 Ruby 中,除了 falsenil之外的所有值在默认情况下都为 true,所以您只需要将解析添加到 String 中。

这种方法可行:

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

When used, this would give you:

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 背后的部分“伟大想法”就是让你想要的行为成为你程序的固有特性(比如布尔解析) ,而不是创建一个完全封装的类,生活在它自己的名称空间世界里(比如布尔解析器)。

Ruby 论坛(2013)上引用马茨的话:

...There's nothing true and false commonly share, thus no Boolean class. 除此之外,在 Ruby 中,一切都表现为布尔值... ..。

正如其他人所说,您可以“补丁”Ruby。创建自己的类。这是我想到的。布尔类上的方法有点傻,但在某些时候它们可能在编程上很有用。

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

TrueClassFalseClass与某种 Boolean超类结合在一起,就破坏了 LSP (Liskov代换原则,< a href = “/questions/tagged/LSP”class = “ post-tag”title = “ show questions tag & # 39; LSP & # 39;”rel = “ tag”> LSP )-那么“任何使用布尔值的代码都应该能够透明地使用它的任何子代”。这是不可能实现的 TrueClassFalseClass,因为他们有相反的行为。

顺便说一下,LSP 是 很可靠(L)

看起来它是 LSP 在现实生活中的一个很好的例子。

UPD # 1 : 关于 LSP 是否破碎

COMMON_EXPECTED_VALUE = x


def test_LSP(klass)
assert klass.new.value == COMMON_EXPECTED_VALUE
end


test_LSP(FooClass)
test_LSP(BarClass)

一旦你得到了两个绿色-你有机会不破坏 LSP 加入这个类与公共超类,当然,如果你将得到双绿色的所有功能的未来超类。