没有`Ord`的类似数据结构?

时间:2015-02-22 16:58:46

标签: haskell set

考虑以下类型:

import Data.Set as Set

-- http://json.org/

type Key = String

data Json = JObject Key (Set JValue)
            | JArray JArr
            deriving Show

data JObj = JObj Key JValue
            deriving Show

data JArr = Arr [JValue] deriving Show

data Null = Null deriving Show

data JValue = Num Double
              | S String
              | B Bool
              | J JObj
              | Array JArr
              | N Null
               deriving Show

我使用单个元素创建了JObject Key (Set Value)

ghci> JObject "foo" (Set.singleton (B True))
JObject "foo" (fromList [B True])

但是,当我尝试创建一个2元素集时,我遇到了一个编译时错误:

ghci> JObject "foo" (Set.insert (Num 5.5) $ Set.singleton (B True))

<interactive>:159:16:
    No instance for (Ord JValue) arising from a use of ‘insert’
    In the expression: insert (Num 5.5)
    In the second argument of ‘JObject’, namely
      ‘(insert (Num 5.5) $ singleton (B True))’
    In the expression:
      JObject "foo" (insert (Num 5.5) $ singleton (B True))

所以我问道,“为什么JValue有必要实现Ord类型类?”

Data.Set上的文档回答了这个问题。

  

Set的实现基于大小平衡的二叉树(或有界平衡树)

但是,是否有一个类似Set的,即非有序的数据结构,不需要Ord的实现,我可以使用?

1 个答案:

答案 0 :(得分:8)

您几乎总是需要至少Eq来实现一个集合(或至少能力来编写Eq实例,无论其是否存在)。只有 Eq会给你一个可怕的低效率。您可以使用OrdHashable来改善这一点。

你可能想要做的一件事是使用一个trie,它可以让你利用嵌套结构,而不是经常对抗它。

您可以先查看generic-trie。这似乎不会为您的Array件提供任何内容,因此您可能需要添加一些内容。

为什么Eq不够好

实现集合的最简单方法是使用列表:

type Set a = [a]

member a [] = False
member (x:xs) | a == x = True
              | otherwise = member a xs

insert a xs | member a xs = xs
            | otherwise = a:xs

这是不好的(除非元素很少),因为你可能必须遍历整个列表以查看是否有成员。

为了改善问题,我们需要使用某种树:

data Set a = Node a (Set a) (Set a) | Tip

我们可以制作许多不同种类的树,但为了使用它们,我们必须能够在每个节点决定采用哪些树枝。如果我们只有Eq,则无法选择正确的选项。如果我们有Ord(或Hashable),则会为我们提供一种选择方式。

trie方法根据数据结构构造树。如果您的类型是深层嵌套的(列表记录数组列表...),则散列或比较可能非常昂贵,因此特里可能会更好。

关于Ord

的附注

虽然我认为你不应该在这里使用Ord方法,但它通常是正确的方法。在某些情况下,您的特定类型可能不会具有自然排序,但有一些有效的方式来对其元素进行排序。在这种情况下,您可以使用newtype

进行操作
newtype WrappedThing = Wrap Thing

instance Ord WrappedThing where
  ....

newtype ThingSet = ThingSet (Set WrappedThing)
insertThing thing (ThingSet s) = ThingSet (insert (Wrap thing) s)
memberThing thing (ThingSet s) = member (WrapThing) s
...

在某些情况下,另一种方法是定义一个“基类型”,它是一个Ord实例,但只导出一个newtype包装器;您可以对所有内部函数使用基类型,但导出的类型是完全抽象的(而不是Ord实例)。