很少有人希望在产品数据类型上实现<=>
(比较或“飞船”)运算符,即具有多个字段的类(所有这些(我们希望!有<=>
实施),比较按特定顺序的字段。Ruby的实现<=> Combinator
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]
(对于那些不熟悉fold
,上述膨胀以
comparing f1 `mappend` comparing f2 `mappend` comparing f3
其产生可被应用到两个功能D
s,产生一个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'。
这个版本意味着所有对象的respond_to?(:<=>),但大多数人会提出一个NoMethodError。这不是一个好主意。您可以尝试将:<=>的定义移动到:spaceship_uses调用中,以解决问题。只需使用define_method(:<=>)do ... end – 2009-05-20 03:51:33
也不要忘记包含Comparagble,所以您的其他比较运算符是为您定义的。 – rampion 2009-05-20 12:45:22
这是一个修改,其中包括我和Gaius的变化:http://gist.github.com/114798 – rampion 2009-05-20 13:04:39