为什么Data.Set需要元素作为Ord的实例?

Cod*_*ace 20 haskell set

这不起作用

data Cutlery = Knife | Fork deriving (Show,Eq)
let x = [Knife,Fork]
let set1 = Set.fromList x
Run Code Online (Sandbox Code Playgroud)

在定义时

data Cutlery = Knife | Fork deriving (Show,Ord,Eq)
Run Code Online (Sandbox Code Playgroud)

解决了这个问题,但没有意义.Data.Set是否与集合的数学定义不同?

Chr*_*lor 19

A Data.Set捕获集合的数学抽象,但它不相同.主要区别在于a Data.Set要求对其元素进行排序,而数学集只要求其元素在相等性方面具有可比性.

要求的原因Ord是效率.通过定义构建集合抽象是完全可能的

data Set a = Set [a]
Run Code Online (Sandbox Code Playgroud)

即在引擎盖下它只是一个列表,我们确保我们永远不会插入重复的元素.在eleminsert操作将是

elem a (Set as) = any (a ==) as

insert a (Set as) | a `elem` as = Set as
                  | otherwise   = Set (a:as)
Run Code Online (Sandbox Code Playgroud)

然而,这意味着,eleminsert为O(ñ)操作.如果我们想要做得比这更好,标准方法是

  1. 将元素存储在平衡的二叉树中(需要一个Ord实例)
  2. 散列元素并将它们存储在一个数组中(需要一个Hashable实例).

TreeSet中

作者选择的实现Data.Set是使用二叉树,您可以通过转到来查看.实施或多或少

data Set a = Bin a (Set a) (Set a)
           | Tip
Run Code Online (Sandbox Code Playgroud)

现在您可以将elem函数编写为

elem :: Ord a => a -> Set a -> Bool
elem = go
  where
    go _  Tip = False
    go x (Bin y l r) = case compare x y of
      LT -> go x l
      GT -> go x r
      EQ -> True
Run Code Online (Sandbox Code Playgroud)

这是一个O(log n)操作,而不是O(n).插入比较棘手(因为你需要保持树平衡)但类似.

HashSet的

在哈希集中,在插入和删除元素时不直接比较元素.相反,每个元素都被散列为整数,并存储在基于该整数的位置.

从理论上讲,这不需要Ord实例.在实践中,您需要一些方法来跟踪散列到相同值的多个元素,开发人员选择的方法Data.HashSet是将多个元素存储在常规中Data.Set,因此事实证明您确实需要Ord实例!

data HashSet a = HashSet (Data.IntMap.IntMap (Data.Set.Set a))
Run Code Online (Sandbox Code Playgroud)

它原本可以写成

data HashSet a = HashSet (Data.IntMap.IntMap [a])
Run Code Online (Sandbox Code Playgroud)

相反,Ord如果存在许多具有相同值的元素,则以某种低效率为代价来消除需求.

  • @ medPhys-pl:HashSet的元素需要可以清除.这对于[Haskell实现](http://hackage.haskell.org/package/hashmap-1.3.0.1/docs/Data-HashSet.html)以及Java版本都是如此,只有我认为Java作弊散列_memory adresses_,Haskell避免因为它不安全(相同的值可能有不同的散列). (2认同)
  • @leftaroundabout:实际上,在Java中,`Object`类有`hashCode`和`equals`方法,`HashMap` /`HashSet`使用那些; 你应该覆盖它们,以便通过基于散列的集合获得正确的行为.这导致了我在Java新手中看到的三个最常见的错误:(a)不知道或忘记为你的地图键实现`hashCode`和`equals`; (b)错误地实现`hashCode`方法; (c)将`hashCode`实现为`return toString().hashCode()`,性能可怕...... (2认同)

Joa*_*ner 7

Data.Set不是比一组的数学定义?

显然,数学集可以是无数的 - 你无法用计算机,甚至图灵机来表示这一点.

但您要寻找的答案是:Data.Set基于二叉树的数据类型,需要元素的总线性顺序,以了解是否放置并稍后在节点的左侧或右侧子树上找到某些内容.因此,虽然可以在没有Ord约束的情况下实现set数据类型,但这种特殊的,更有效的实现不会.