Ответ 1
A Data.Set
фиксирует математическую абстракцию набора, но не идентична. Основное отличие состоит в том, что a Data.Set
требует, чтобы его элементы упорядочивались, тогда как математический набор требовал, чтобы его элементы были сопоставимы для равенства.
Причиной для требования Ord
является эффективность. Было бы вполне возможно построить абстрактную абстракцию, указав
data Set a = Set [a]
то есть. под капотом это всего лишь список, и мы убеждаемся, что мы никогда не вставляем повторяющиеся элементы. Операции elem
и insert
были бы
elem a (Set as) = any (a ==) as
insert a (Set as) | a `elem` as = Set as
| otherwise = Set (a:as)
Однако это означает, что оба elem
и insert
являются операциями O (n). Если мы хотим сделать что-то лучше этого, стандартными подходами являются
- Храните элементы в сбалансированном двоичном дереве (для которого требуется экземпляр
Ord
) - Хешируйте элементы и храните их в массиве (для чего требуется экземпляр
Hashable
).
TreeSet
Реализация, выбранная авторами Data.Set
, заключалась в использовании двоичного дерева, которое вы можете увидеть, перейдя в источник . Реализация более или менее
data Set a = Bin a (Set a) (Set a)
| Tip
Теперь вы можете написать функцию 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
который является операцией O (log n), а не O (n). Вставки сложнее (так как вам нужно держать дерево сбалансированным), но схожим.
HashSet
В наборе хэшей вы не сравниваете элементы напрямую при их вставке и удалении. Вместо этого каждый элемент хэшируется до целого числа и сохраняется в местоположении на основе этого целого.
Теоретически это не требует экземпляра Ord
. На практике вам нужен какой-то способ отслеживания нескольких элементов, хэш которых имеет одинаковое значение, а метод, выбранный разработчиками Data.HashSet
, состоит в том, чтобы хранить несколько элементов в регулярном Data.Set
, поэтому, оказывается, вам нужно экземпляр Ord
в конце концов!
data HashSet a = HashSet (Data.IntMap.IntMap (Data.Set.Set a))
Его можно было бы написать вместо
data HashSet a = HashSet (Data.IntMap.IntMap [a])
вместо этого, который удаляет требование Ord
за счет некоторой неэффективности, если существует много элементов, имеющих одинаковое значение.