Ruby 是有很多语言发展而来的,它在借鉴了这些语言的很多特性的同时,也有很多自己处理问题的方式,有时候这些方式会令你困惑或惊讶。
True & False
首先先来看一下 ruby 中的 True 和 False。在处理真值假值上面,很多语言都有着不同的处理方式。在我以前用过的语言中,就有很多 0 表示假的例子,比如 c 语言中的定义: 0 为 false,!0 (即所有非 0 值)为 true 。
那么在 ruby 中是怎样呢,实际上在 ruby 中区别真假值的规则非常简单,有别于其它很多语言,ruby 中的定义是这样的: 除了 false 和 nil ,其余的所有值都是真!没错,0 值也是真,这一点曾令我栽过不止一次跟头。
并且在 ruby 中,true 和 false 不同于很多其它语言,它们不是关键字,当你查看它们的 class 时,会发现
true.class
# => TrueClass
false.class
# => FalseClass
可以看出,true 和 false 和普通的对象一样,可以调用在它之上的任意方法,同时 ruby 也定义了 TRUE 和 FALSE 常量,而它们仅仅是对这些 true 和 false 对象的引用。其实在上边就已经说过了,在 ruby 中除了 false 和 nil 之外的任何对象都可以表示真值,因此定义的 true 对象来专门表示真值是冗余的,true 对象的存在其实只是单纯的为了方便而已。
但是这个时候又会引出另一个困惑,那就是有时我们需要去区别 false 和 nil,这个时候通常会选用两种方式来判断: nil? 或 ‘==’.
并且在使用 ‘==’ 方法的时候,书中建议把 false 作为左操作对象。这并不是有些语言中一些建议规范或 style 一样的建议,而是这样做是有其用意的。当你把 false 作为左值的时候其实就相当于调用 FalseClass#== 这个方法,这样可以很放心的确定如果右边的操作对象也是 false 对象时,返回 true。而如果把待比较对象置于左位置,那么就相当于调用该待比较对象的 ‘==’ 方法,当然其也是继承自 Object 方法,但是如果它复写了 Object#== 方法的话,就有可能导致出现预期以外的结果,这是我们不想看到的。
nil
而对于 nil 来说,还有另外一些问题。在 Ruby 的类型系统的运作方式下,任何对象都可以是 nil,因为 Ruby 程序中的每一个对象都源自 BasicObject 这个类,那么也就是说在程序运行过程中一个类的对象是能够被另一个类的对象轻易替换的。在Ruby程序中,相比于当前对象是什么(is_a?),它更在意当前对象能够响应什么方法(respond_to?)。当该对象无法响应其方法时,就抛出 error。
在程序中,很多时候一些错误都产生自
undefined method xxx for nil::NilClass
真是令人讨厌呀,这就导致当你在定义方法时最好都假设任何对象都可以为nil并做好处理工作,比如做 nil? 等这类判断。 或者很多时候还使用 Object 定义的几种基础转换方法: to_s, to_i 等。因为令人愉悦的,NilClass 也是可以正常响应这类方法的,比如 nil.to_s 就会返回一个空字符串。另外还有一点要指出, 比如 String#to_s 方法只是简单的返回 self 而不会去做任何的转换和复制,这样的就有一大好处,当 to_s 的调用方本身就是一个 string 时,那么此方法的开销相当的小。
几种不同的等价判断
和其它很多语言一样,在 Ruby 中也存在数种等价比较的方法,有时在不注意的时候就会因为对某种方法的误用得到意料之外的结果。这次就来把这几种方法扒拉扒拉清楚。
首先先来看看 ’==’ 和 ‘equal?’
’==’ 很常用,也很简单,它的惯例行为是当两个对象表示相同的值时返回真值。就比如
'abc' == 'abc'
# => true
还有一点,平时经常会用到但是可能并没有注意到的是,很多表示数字的类在使用 ’==’ 操作符比较对象时通常会做类型的隐式转换。
1 == 1.0
# => true
而 ‘equal?’ 方法就与此完全不同,就比如当你比较两个字符串时:
'abc' == 'abc'
# => true
'abc'.equal? 'abc'
# => false
发现它们并不相等。equal? 方法其实并不是比较的两个对象的内容和值,而是检查它们是否为同一个对象,也就是说当你使用 equal? 方法时,只有当两者具有相同的 object_id 时,返回值才为真,它其实检查的是两个对象的指针是否指向内存中的同一块地址。
还有一个方法 ‘eql?’ ,这个方法可能你并没有显示的使用过它,但是它在 Hash 类中被广泛的用于比较对象的键,以确保同一个键不会被插入多次。
‘eql?’ 方法的默认行为和 ‘equal?’ 是一样的,那么它在 Hash 类中到底是起什么作用呢,我们先来看另外一个方法 hash ,这个方法决定了将对象存在数据结构的什么地方。当两个对象表示相同的键时它们的 hash 方法应该返回相同的值,但是有时不同的对象在调用 hash 方法时也可能会返回相同的值,这就是哈希冲突,在这里就不过多阐述这方面相关的东西了。接下来说 eql? 方法,当 hash 值一致就会进一步通过 eql? 方法来确定其是否真的具有相同的键值。