2015-03-08 53 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

相關問題