不经常,人们想要实现< => (产品数据类型上的比较或“太空船”)运算符,即具有多个字段的类(所有这些(我们希望!)已经具有< =>实现),以特定顺序比较字段.
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
这既繁琐又容易出错,尤其是在很多领域.它容易出错,我经常觉得我应该对这个功能进行单元测试,这只会增加乏味和冗长.
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]
(对于那些不熟悉折叠的人,上面扩展到了
comparing f1 `mappend` comparing f2 `mappend` comparing f3
它产生一个可以应用于两个D的函数,以产生一个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代码有多接近,以及它的可靠性如何?如有必要,如何确保字段具有正确实现的< =>或者<和>操作符?
很明显,虽然这是一个Ruby问题,但如果本网站的长老如此同意,我很乐意考虑讨论Haskell技术.请随意评论这是否合适,如果是,则标记此帖子’haskell’.
解决方法
这是你的想法的一个即兴重复.它没有定义任何额外的常量,允许您使用实例变量和方法的任意组合来比较两个对象,提前退出不相等,并包括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,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,2) x = T.new('x',2,3) y = T.new('y',3,4) z = T.new('z',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
如果您愿意,可以在其中插入一些额外的错误检查以确保这一点用于比较的值实际上响应< => (使用respond_to?’< =>‘),并尝试在他们没有的情况下给出更明确的错误消息.