使用不同的集合排序

时间:2017-01-20 07:03:33

标签: haskell

我正在阅读纯功能数据结构的第2章,其中讨论了作为二叉搜索树实现的无序集。代码以ML编写,最终显示signature ORDEREDfunctor UnbalancedSet(Element: ORDERED): SET。来自更多的C ++背景,这对我来说很有意义;自定义比较函数对象构成了类型的一部分,可以在构造时传入,这看起来非常类似于ML函数的处理方式。

当谈到Haskell时,似乎行为仅取决于Ord实例,所以如果我想要一个其顺序颠倒的集合,似乎我必须使用{{ 1}}实例,例如

newtype

然后我可以在一个集合中使用:

newtype ReverseInt = ReverseInt Int deriving (Eq, Show)

instance Ord ReverseInt where
    compare (ReverseInt a) (ReverseInt b)  
        | a == b = EQ
        | a < b  = GT
        | a > b  = LT

有没有更好的方法来做这种事情,而不是使用let x = Set.fromList $ map ReverseInt [1..5] 来创建不同的newtype实例?

1 个答案:

答案 0 :(得分:9)

不,这真的是要走的路。是的,有一个newtype有时很烦人,但你会得到一些好处:

  • 当您看到Set a而您知道a时,您会立即知道它使用的比较类型(与纯度使代码更具可读性的方式相同,因为您不必跟踪执行)。您不必知道Set a来自何处。
  • 在很多情况下,您可以coerce一次通过多种新类型。例如,我可以使用xs = [1,2,3] :: Intys = [ReverseInt 1, ReverseInt 2, ReverseInt 3] :: [ReverseInt]转换为ys = coerce xs :: [ReverseInt]。不幸的是,Set的情况并非如此(并且它不应该 - 你需要强制函数是单调的,以免搞砸数据结构不变量,并且没有还有一种在类型系统中表达的方法。)
  • newtype最终会比你期望的更具有组合性。例如,您创建的ReverseInt类型已经存在于一种形式,该形式可以推断为使用Ord约束来反转任何类型:它被称为Down。要明确,您可以使用Down Int代替ReversedInt,然后获得免费写的实例!

当然,如果您对此仍然非常强烈,那么没有什么能阻止您编写您的Set版本,该版本必须具有它所使用的比较函数的字段。像

这样的东西
data Set a = Set { comparisionKey :: a -> a -> Ordering
                 , ...
                 }

然后,每次制作Set时,都必须传入比较密钥。