在 Ruby (1.9.3) 中,为什么 nil 响应比较运算符,`<=>`?
In Ruby (1.9.3), why does nil respond to the comparison operator, `<=>`?
对我来说,将 null 类型与其他任何东西(甚至是另一个 null 类型)进行比较是未定义的操作。如有不妥请指正
根据该假设,以下内容对我来说有意义:
nil.is_a? Comparable
=> false
nil.respond_to? :<=
=> false
nil.respond_to? :<
=> false
nil.respond_to? :>=
=> false
nil.respond_to? :>
=> false
但是,nil
确实响应"spaceship"比较运算符:
nil.respond_to? :<=>
=> true
我想不出比较 nil
有什么意义,更不用说实用了。为什么 nil
有这种行为?
nil
in Ruby 是 NilClass
的单例实例,它继承自 Object
。对象实现 <=>
,其行为定义为:
Returns 0 if obj and other are the same object or obj == other, otherwise nil. 0 means self is equal to other. 1 means self is bigger than other. Nil means the two values could not be compared.
因此,nil <=> nil
returns 0(它们是等价的),但是nil <=> anything_else
returns nil
,这意味着"could not be compared" .
在Ruby中,期望所有对象都响应<=>
(包括nil
),但对于无意义或未定义操作的对象,return 的值为 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]
Object#<=> 对 nil
有多大用处?我想这只是受限于一个人的想象力。
示例 #1
这里有一个简单的例子来说明它的用处。假设您希望对数组进行排序:
arr = [1,nil,3,nil,2]
所有 nil
排在第一位,所以它 return:
[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"]
单看世界上名人的数量,以及对待他们的方式,我认为这是一个非常有用的方法。
应用前面的例子,我们得到:
[1,nil,3,nil,2].sort_by_nils_first(&:itself)
#=> [nil, nil, 1, 2, 3]
我在 v2.2.
中使用了 Object#itself
sort_by_nils_first
当然可以在没有给出块时修改为 return 和 Enumerator
,使其与 Enumerable#sort_by.
相当
对我来说,将 null 类型与其他任何东西(甚至是另一个 null 类型)进行比较是未定义的操作。如有不妥请指正
根据该假设,以下内容对我来说有意义:
nil.is_a? Comparable
=> false
nil.respond_to? :<=
=> false
nil.respond_to? :<
=> false
nil.respond_to? :>=
=> false
nil.respond_to? :>
=> false
但是,nil
确实响应"spaceship"比较运算符:
nil.respond_to? :<=>
=> true
我想不出比较 nil
有什么意义,更不用说实用了。为什么 nil
有这种行为?
nil
in Ruby 是 NilClass
的单例实例,它继承自 Object
。对象实现 <=>
,其行为定义为:
Returns 0 if obj and other are the same object or obj == other, otherwise nil. 0 means self is equal to other. 1 means self is bigger than other. Nil means the two values could not be compared.
因此,nil <=> nil
returns 0(它们是等价的),但是nil <=> anything_else
returns nil
,这意味着"could not be compared" .
在Ruby中,期望所有对象都响应<=>
(包括nil
),但对于无意义或未定义操作的对象,return 的值为 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]
Object#<=> 对 nil
有多大用处?我想这只是受限于一个人的想象力。
示例 #1
这里有一个简单的例子来说明它的用处。假设您希望对数组进行排序:
arr = [1,nil,3,nil,2]
所有 nil
排在第一位,所以它 return:
[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"]
单看世界上名人的数量,以及对待他们的方式,我认为这是一个非常有用的方法。
应用前面的例子,我们得到:
[1,nil,3,nil,2].sort_by_nils_first(&:itself)
#=> [nil, nil, 1, 2, 3]
我在 v2.2.
中使用了 Object#itselfsort_by_nils_first
当然可以在没有给出块时修改为 return 和 Enumerator
,使其与 Enumerable#sort_by.