Cur*_*son 6 ruby combinators spaceship-operator
不经常,人们希望<=>在产品数据类型上实现(比较或"太空船")运算符,即具有多个字段的类(所有这些(我们希望!)已经<=>实现),比较某些字段订购.
def <=>(o)
f1 < o.f1 && (return -1)
f1 > o.f1 && (return 1)
f2 < o.f2 && (return -1)
f2 > o.f2 && (return 1)
return 0
end
Run Code Online (Sandbox Code Playgroud)
这既繁琐又容易出错,尤其是在很多领域.它容易出错,我经常觉得我应该对这个功能进行单元测试,这只会增加乏味和冗长.
Haskell提供了一种特别好的方法:
import Data.Monoid (mappend)
import Data.Ord (comparing)
-- From the standard library:
-- data Ordering = LT | EQ | GT
data D = D { f3 :: Int, f2 :: Double, f1 :: Char } deriving Show
compareD :: D -> D -> Ordering
compareD = foldl1 mappend [comparing f1, comparing f2, comparing f3]
(对于那些不熟悉的人fold,上面扩展到了
comparing f1 `mappend` comparing f2 `mappend` comparing f3
Run Code Online (Sandbox Code Playgroud)
它产生一个可以应用于两个Ds 的函数,以产生一个Ordering.)
定义compareD非常简单,显然是正确的,即使没有静态类型检查,我也不觉得需要进行单元测试.
实际上,问题甚至可能比这更有趣,因为我可能不想仅使用标准<=>运算符,而是在不同时间以不同方式排序,例如:
sortByOrderings :: [a -> a -> Ordering] -> [a] -> [a] sortByOrderings = sortBy . foldl1 mappend sortByF3F1 = sortByOrderings [comparing f3, comparing f1] sortByF2F3 = sortByOrderings [comparing f2, comparing f3]
所以,问题:
<=>或<与>运营商?很明显,虽然这是一个Ruby问题,但如果本网站的长老如此同意,我很乐意考虑讨论Haskell技术.请随意评论这是否合适,如果是,则标记此帖子'haskell'.
以下是我使自定义排序规则更易于管理的方法:在我需要排序的所有类中,我定义了返回数组的"to_sort"方法,然后重写<=>以使用to_sort:
class Whatever
def to_sort
[@mainkey,@subkey,@subsubkey]
end
def <=>(o)
self.to_sort <=> o.to_sort
end
end
Run Code Online (Sandbox Code Playgroud)
因此,排序任何Whatevers数组(包括Whatevers和Whateverothers和Whathaveyours的异构数组,所有这些都实现特定于类型的to_sort函数和同样的<=>覆盖)只是在内部转移到排序数组数组.
这是你的想法的一个即兴重复.它没有定义任何额外的常量,允许您使用实例变量和方法的任意组合来比较两个对象,提前退出不相等,并包括Comparable定义的所有方法.
class Object
def self.compare_by(*symbols)
include Comparable
dispatchers = symbols.map do |symbol|
if symbol.to_s =~ /^@/
lambda { |o| o.instance_variable_get(symbol) }
else
lambda { |o| o.__send__(symbol) }
end
end
define_method('<=>') do |other|
dispatchers.inject(0) do |_,dispatcher|
comp = dispatcher[self] <=> dispatcher[other]
break comp if comp != 0
comp
end
end
end
end
class T
def initialize(name,f1,f2,f3)
@name,@f1, @f2, @f3 = name,f1, f2, f3;
end
def f1
puts "checking #@name's f1"
@f1
end
def f3
puts "checking #@name's f3"
@f3
end
compare_by :f1, :@f2, :f3
end
w = T.new('x',1,1,2)
x = T.new('x',1,2,3)
y = T.new('y',2,3,4)
z = T.new('z',2,3,5)
p w < x #=> checking x's f1
# checking x's f1
# true
p x == y #=> checking x's f1
# checking y's f1
# false
p y <= z #=> checking y's f1
# checking z's f1
# checking y's f3
# checking z's f3
# true
Run Code Online (Sandbox Code Playgroud)
如果你愿意,你可以在那里插入一些额外的错误检查,以确保用于比较的值实际上响应<=>(使用respond_to? '<=>'),并尝试在它们没有的情况下给出更清晰的错误消息.