2015-03-08 52 views
2

对我来说,一个空类型与其他任何东西(甚至另一个空类型)进行比较都是未定义的操作。如果我在那里错了,请纠正我。在Ruby(1.9.3)中,为什么nil没有响应比较运算符'<=>`?

根据这一假设,下面对我来说很有意义:

nil.is_a? Comparable 
=> false 

nil.respond_to? :<= 
=> false 

nil.respond_to? :< 
=> false 

nil.respond_to? :>= 
=> false 

nil.respond_to? :> 
=> false 

然而,nil确实响应的 “宇宙飞船” 比较操作:

nil.respond_to? :<=> 
=> true 

我想不出的情况那里比较nil甚至是有意义的,更不用说实用。为什么nil有这种行为?

回答

5

nil Ruby中是一个单例实例NilClass,它继承自Object。对象实现<=>,其行为定义如下:

如果obj和其他是相同的对象或obj == other,则返回0否则为零。 0表示自己等于其他。 1表示自己比其他人大。无意味着两个值无法比较。

(参见the documentation

因此,nil <=> nil返回0(它们是等价的),但nil <=> anything_else返回nil,这意味着 “无法比拟的”。

在Ruby中,可以预期,所有对象响应<=>(包括nil),但对于对象为它是一个无意义的或不确定的操作,则返回值为nil,其可随后作为调用代码最好被处理看起来合适。在Enumerable的操作像#sort的情况下,它引发了一个异常:

[1, nil].sort 
# => ArgumentError: comparison of NilClass with 1 failed 

但它不一定;您可以实现自己的排序这只是移动不可排序值到列表的开头:

[1, nil, 2, 3, nil].sort {|a, b| (a <=> b) || -1 } 
# => [nil, nil, 1, 2, 3] 
+0

那么解释行为。在你看来,有没有一种情况是有用的?或者,这仅仅是任何语言的陷阱之一? – kdbanman 2015-03-08 20:02:47

+1

我认为这是微不足道的;它的存在使得如果你对于事情的表现应该有不同的意见,那么你可以实现这些意见,但我同意在大多数情况下它通常没有用。 – 2015-03-08 20:05:12

1

如何有用的是Object#<=>nil?我认为这只受到人们的想象力的限制。

实施例#1

下面是如何可以是有用的行人的例子。假设你想对数组进行排序:

arr = [1,nil,3,nil,2] 

所有的nil来临了第一,所以它会返回:

[nil, nil, 1, 2, 3] 

由于:

nil<=>nil #=> 0 

,并为所有非nil物体a

nil<=>x #=> nil 
x<=>nil #=> nil 

,我们可以这样写:

arr.sort { |a,b| (a<=>b) ? a<=>b : a.nil? ? -1 : 1 } 
    #=> [nil, nil, 1, 2, 3] 

例2

现在让我们来看看这是更有趣的第二个例子。假设我们有超卖门票的戏剧表演,必须拒绝一些顾客,并给他们退款。哈希tickets显示了他们的票付出什么每个人:

ticket_holders = { 'bob'=>10, 'lucy'=>15, 'cher'=>5, 'arnold'=>12 } 

我们希望最小化发行的退款,但不希望从转身离开名人的负面宣传,由下式给出:

celebrities = ['arnold', 'cher'] 

所以我们会给他们最高的偏好。因此,我们希望按降序对ticket_holders进行排序,除了我们希望键为celebrities之前的键值对。也就是说,我们希望得到的结果是:

['cher', 'arnold', 'lucy', 'bob'] 

['arnold', 'cher', 'lucy', 'bob'] 

我们去一个通用的解决方案:

module Enumerable 
    def sort_by_nils_first 
    sort do |a,b| 
     av = yield(a) 
     bv = yield(b) 
     (av<=>bv) ? av<=>bv : av.nil? ? -1 : 1 
    end 
    end 
end 

我们因此适用:

ticket_holders.sort_by_nils_first { |name,price| 
    celebrities.include?(name) ? nil : -price }.map(&:first) 
    #=> ["arnold", "cher", "lucy", "bob"] 

仅考虑celebriti的数量在这个世界上,以及如何对待他们,我认为这是一个非常有用的方法。

应用到前面的例子中,我们得到:

[1,nil,3,nil,2].sort_by_nils_first(&:itself) 
    #=> [nil, nil, 1, 2, 3] 

,我已经使用从V2.2 Object#itself

sort_by_nils_first当然可以修改为当没有给出块时返回Enumerator,以使其与Enumerable#sort_by相当。

+0

heh。在看到你的答案之前,我选择了你所做的确切例子。滑稽! – 2015-03-08 20:04:37

+0

@ChrisHeald,是的,我注意到他们同时出现。然而,我认为我的第二个例子更有趣。 – 2015-03-08 20:57:53

相关问题