Skip to content

Commit 7b6a824

Browse files
committed
Improve HashSet API docs.
1 parent c03be06 commit 7b6a824

File tree

2 files changed

+176
-47
lines changed

2 files changed

+176
-47
lines changed

Data/HashSet.hs

Lines changed: 62 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -4,25 +4,68 @@
44
#endif
55

66
------------------------------------------------------------------------
7-
-- |
8-
-- Module : Data.HashSet
9-
-- Copyright : 2011 Bryan O'Sullivan
10-
-- License : BSD-style
11-
-- Maintainer : [email protected]
12-
-- Stability : provisional
13-
-- Portability : portable
14-
--
15-
-- A set of /hashable/ values. A set cannot contain duplicate items.
16-
-- A 'HashSet' makes no guarantees as to the order of its elements.
17-
--
18-
-- The implementation is based on /hash array mapped trie/. A
19-
-- 'HashSet' is often faster than other tree-based set types,
20-
-- especially when value comparison is expensive, as in the case of
21-
-- strings.
22-
--
23-
-- Many operations have a average-case complexity of /O(log n)/. The
24-
-- implementation uses a large base (i.e. 16) so in practice these
25-
-- operations are constant time.
7+
{-|
8+
Module : Data.HashSet
9+
Copyright : 2011 Bryan O'Sullivan
10+
License : BSD-style
11+
Maintainer : [email protected]
12+
Stability : provisional
13+
Portability : portable
14+
15+
= Introduction
16+
17+
'HashSet' allows you to store /unique/ elements, providing efficient
18+
insertion, lookups, and deletion. If you are storing sets of @Int@s consider
19+
using the @Data.IntSet@ from the
20+
<https://hackage.haskell.org/packages/containers containers> package.
21+
A 'HashSet' makes no guarantees as to the order of its elements.
22+
23+
== Immutability
24+
25+
@HashSet@s are /immutable/ which means that any update functions do not modify
26+
the set you passed in, they create a new set. In order to keep the changes
27+
you need to assign it to a new variable.
28+
29+
== Example
30+
31+
@
32+
import qualified Data.HashSet as HashSet
33+
34+
-- Create an empty HashSet.
35+
let s = HashSet.'HashSet.empty'
36+
37+
-- Create a HashSet from a list of elements.
38+
let s1 = HashSet.'HashSet.fromList' ["a", "b"]
39+
40+
-- Remove an entry from the HashSet @s1@, storing the new set in @s2@.
41+
let s2 = HashSet.'HashSet.delete' "a" s1
42+
43+
-- Print the @HashSet@ @s1@, notice it still contains the deleted @"a"@.
44+
print s1
45+
> HashSet.'HashSet.fromList' ["a","b"]
46+
47+
-- Print the modified @HashSet@, notice that it does not contain the deleted @"a"@.
48+
print s2
49+
> HashSet.'HashSet.fromList' ["b"]
50+
@
51+
52+
__IMPORTANT:__ @HashSet@ relies on the @element@ type having instances of the
53+
@Eq@ and @Hashable@ typeclasses for its internal representation. These are
54+
already defined for builtin types, and if you are using your own data type you
55+
can use the <https://en.wikibooks.org/wiki/Haskell/Classes_and_types#Deriving deriving>
56+
mechanism.
57+
58+
== Performance
59+
60+
The implementation is based on /hash array mapped trie/. A
61+
'HashSet' is often faster than other tree-based set types,
62+
especially when value comparison is expensive, as in the case of
63+
strings.
64+
65+
Many operations have a average-case complexity of /O(log n)/. The
66+
implementation uses a large base (i.e. 16) so in practice these
67+
operations are constant time.
68+
-}
2669

2770
module Data.HashSet
2871
(

Data/HashSet/Base.hs

Lines changed: 114 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -36,10 +36,6 @@ module Data.HashSet.Base
3636
, empty
3737
, singleton
3838

39-
-- * Combine
40-
, union
41-
, unions
42-
4339
-- * Basic interface
4440
, null
4541
, size
@@ -50,6 +46,10 @@ module Data.HashSet.Base
5046
-- * Transformations
5147
, map
5248

49+
-- * Combine
50+
, union
51+
, unions
52+
5353
-- * Difference and intersection
5454
, difference
5555
, intersection
@@ -259,38 +259,63 @@ fromListConstr = mkConstr hashSetDataType "fromList" [] Prefix
259259
hashSetDataType :: DataType
260260
hashSetDataType = mkDataType "Data.HashSet.Base.HashSet" [fromListConstr]
261261

262-
-- | /O(1)/ Construct an empty set.
262+
-- | Construct an empty set.
263+
--
264+
-- >>> HashSet.empty
265+
-- fromList []
266+
--
267+
-- __Complexity:__ /O(1)/
263268
empty :: HashSet a
264269
empty = HashSet H.empty
265270

266-
-- | /O(1)/ Construct a set with a single element.
271+
-- | Construct a set with a single element.
272+
--
273+
-- >>> HashSet.singleton 1
274+
-- fromList [1]
275+
--
276+
-- __Complexity:__ /O(1)/
267277
singleton :: Hashable a => a -> HashSet a
268278
singleton a = HashSet (H.singleton a ())
269279
{-# INLINABLE singleton #-}
270280

271-
-- | /O(1)/ Convert to the equivalent 'HashMap'.
281+
-- | Convert to set to the equivalent 'HashMap' with @()@ values.
282+
--
283+
-- >>> HashSet.toMap (HashSet.singleton 1)
284+
-- fromList [(1,())]
285+
--
286+
-- __Complexity:__ /O(1)/
272287
toMap :: HashSet a -> HashMap a ()
273288
toMap = asMap
274289

275-
-- | /O(1)/ Convert from the equivalent 'HashMap'.
290+
-- | Convert from the equivalent 'HashMap' with @()@ values.
291+
--
292+
-- >>> HashSet.fromMap (HashMap.singleton 1 ())
293+
-- fromList [1]
294+
--
295+
-- __Complexity:__ /O(1)/
276296
fromMap :: HashMap a () -> HashSet a
277297
fromMap = HashSet
278298

279-
-- | /O(n)/ Produce a 'HashSet' of all the keys in the given 'HashMap'.
299+
-- | Produce a 'HashSet' of all the keys in the given 'HashMap'.
300+
--
301+
-- >>> HashSet.keysSet (HashMap.fromList [(1, "a"), (2, "b")]
302+
-- fromList [1,2]
303+
--
304+
-- __Complexity:__ /O(n)/
280305
--
281306
-- @since 0.2.10.0
282307
keysSet :: HashMap k a -> HashSet k
283308
keysSet m = fromMap (() <$ m)
284309

285-
-- | /O(n+m)/ Construct a set containing all elements from both sets.
310+
-- | Construct a set containing all elements from both sets.
286311
--
287312
-- To obtain good performance, the smaller set must be presented as
288313
-- the first argument.
289314
--
290-
-- ==== __Examples__
291-
--
292315
-- >>> union (fromList [1,2]) (fromList [2,3])
293316
-- fromList [1,2,3]
317+
--
318+
-- __Complexity:__ /O(n+m)/
294319
union :: (Eq a, Hashable a) => HashSet a -> HashSet a -> HashSet a
295320
union s1 s2 = HashSet $ H.union (asMap s1) (asMap s2)
296321
{-# INLINE union #-}
@@ -302,103 +327,164 @@ unions :: (Eq a, Hashable a) => [HashSet a] -> HashSet a
302327
unions = List.foldl' union empty
303328
{-# INLINE unions #-}
304329

305-
-- | /O(1)/ Return 'True' if this set is empty, 'False' otherwise.
330+
-- | Return 'True' if this set is empty, 'False' otherwise.
331+
--
332+
-- >>> HashSet.null HashSet.empty
333+
-- True
334+
-- >>> HashSet.null (HashSet.singleton 1)
335+
-- False
336+
--
337+
-- __Complexity:__ /O(1)/
306338
null :: HashSet a -> Bool
307339
null = H.null . asMap
308340
{-# INLINE null #-}
309341

310-
-- | /O(n)/ Return the number of elements in this set.
342+
-- | Return the number of elements in this set.
343+
--
344+
-- >>> HashSet.size HashSet.empty
345+
-- 0
346+
-- >>> HashSet.size (HashSet.fromList [1,2,3])
347+
-- 3
348+
--
349+
-- __Complexity:__ /O(n)/
311350
size :: HashSet a -> Int
312351
size = H.size . asMap
313352
{-# INLINE size #-}
314353

315-
-- | /O(log n)/ Return 'True' if the given value is present in this
354+
-- | Return 'True' if the given value is present in this
316355
-- set, 'False' otherwise.
356+
--
357+
-- >>> HashSet.member 1 (Hashset.fromList [1,2,3])
358+
-- True
359+
-- >>> HashSet.member 1 (Hashset.fromList [4,5,6])
360+
-- False
361+
--
362+
-- __Complexity:__ /O(log n)/
317363
member :: (Eq a, Hashable a) => a -> HashSet a -> Bool
318364
member a s = case H.lookup a (asMap s) of
319365
Just _ -> True
320366
_ -> False
321367
{-# INLINABLE member #-}
322368

323-
-- | /O(log n)/ Add the specified value to this set.
369+
-- | Add the specified value to this set.
370+
--
371+
-- >>> HashSet.insert 1 HashSet.empty
372+
-- fromList [1]
373+
--
374+
-- __Complexity:__ /O(log n)/
324375
insert :: (Eq a, Hashable a) => a -> HashSet a -> HashSet a
325376
insert a = HashSet . H.insert a () . asMap
326377
{-# INLINABLE insert #-}
327378

328-
-- | /O(log n)/ Remove the specified value from this set if
329-
-- present.
379+
-- | Remove the specified value from this set if present.
380+
--
381+
-- >>> HashSet.delete 1 (HashSet.fromList [1,2,3])
382+
-- fromList [2,3]
383+
-- >>> HashSet.delete 1 (HashSet.fromList [4,5,6])
384+
-- fromList [4,5,6]
385+
--
386+
-- __Complexity:__ /O(log n)/
330387
delete :: (Eq a, Hashable a) => a -> HashSet a -> HashSet a
331388
delete a = HashSet . H.delete a . asMap
332389
{-# INLINABLE delete #-}
333390

334-
-- | /O(n)/ Transform this set by applying a function to every value.
391+
-- | Transform this set by applying a function to every value.
335392
-- The resulting set may be smaller than the source.
393+
--
394+
-- >>> HashSet.map show (HashSet.fromList [1,2,3])
395+
-- HashSet.fromList ["1","2","3"]
396+
--
397+
-- __Complexity:__ /O(n)/
336398
map :: (Hashable b, Eq b) => (a -> b) -> HashSet a -> HashSet b
337399
map f = fromList . List.map f . toList
338400
{-# INLINE map #-}
339401

340-
-- | /O(n)/ Difference of two sets. Return elements of the first set
402+
-- | Difference of two sets. Return elements of the first set
341403
-- not existing in the second.
404+
--
405+
-- >>> HashSet.difference (HashSet.fromList [1,2,3]) (HashSet.fromList [2,3,4])
406+
-- fromList [1]
407+
--
408+
-- __Complexity:__ /O(n)/
342409
difference :: (Eq a, Hashable a) => HashSet a -> HashSet a -> HashSet a
343410
difference (HashSet a) (HashSet b) = HashSet (H.difference a b)
344411
{-# INLINABLE difference #-}
345412

346-
-- | /O(n)/ Intersection of two sets. Return elements present in both
413+
-- | Intersection of two sets. Return elements present in both
347414
-- the first set and the second.
415+
--
416+
-- >>> HashSet.intersection (HashSet.fromList [1,2,3]) (HashSet.fromList [2,3,4])
417+
-- fromList [2,3]
418+
--
419+
-- __Complexity:__ /O(n)/
348420
intersection :: (Eq a, Hashable a) => HashSet a -> HashSet a -> HashSet a
349421
intersection (HashSet a) (HashSet b) = HashSet (H.intersection a b)
350422
{-# INLINABLE intersection #-}
351423

352-
-- | /O(n)/ Reduce this set by applying a binary operator to all
424+
-- | Reduce this set by applying a binary operator to all
353425
-- elements, using the given starting value (typically the
354426
-- left-identity of the operator). Each application of the operator
355427
-- is evaluated before before using the result in the next
356428
-- application. This function is strict in the starting value.
429+
--
430+
-- __Complexity:__ /O(n)/
357431
foldl' :: (a -> b -> a) -> a -> HashSet b -> a
358432
foldl' f z0 = H.foldlWithKey' g z0 . asMap
359433
where g z k _ = f z k
360434
{-# INLINE foldl' #-}
361435

362-
-- | /O(n)/ Reduce this set by applying a binary operator to all
436+
-- | Reduce this set by applying a binary operator to all
363437
-- elements, using the given starting value (typically the
364438
-- right-identity of the operator). Each application of the operator
365439
-- is evaluated before before using the result in the next
366440
-- application. This function is strict in the starting value.
441+
--
442+
-- __Complexity:__ /O(n)/
367443
foldr' :: (b -> a -> a) -> a -> HashSet b -> a
368444
foldr' f z0 = H.foldrWithKey' g z0 . asMap
369445
where g k _ z = f k z
370446
{-# INLINE foldr' #-}
371447

372-
-- | /O(n)/ Reduce this set by applying a binary operator to all
448+
-- | Reduce this set by applying a binary operator to all
373449
-- elements, using the given starting value (typically the
374450
-- right-identity of the operator).
451+
--
452+
-- __Complexity:__ /O(n)/
375453
foldr :: (b -> a -> a) -> a -> HashSet b -> a
376454
foldr f z0 = foldrWithKey g z0 . asMap
377455
where g k _ z = f k z
378456
{-# INLINE foldr #-}
379457

380-
-- | /O(n)/ Reduce this set by applying a binary operator to all
458+
-- | Reduce this set by applying a binary operator to all
381459
-- elements, using the given starting value (typically the
382460
-- left-identity of the operator).
461+
--
462+
-- __Complexity:__ /O(n)/
383463
foldl :: (a -> b -> a) -> a -> HashSet b -> a
384464
foldl f z0 = foldlWithKey g z0 . asMap
385465
where g z k _ = f z k
386466
{-# INLINE foldl #-}
387467

388-
-- | /O(n)/ Filter this set by retaining only elements satisfying a
468+
-- | Filter this set by retaining only elements satisfying a
389469
-- predicate.
470+
--
471+
-- __Complexity:__ /O(n)/
390472
filter :: (a -> Bool) -> HashSet a -> HashSet a
391473
filter p = HashSet . H.filterWithKey q . asMap
392474
where q k _ = p k
393475
{-# INLINE filter #-}
394476

395-
-- | /O(n)/ Return a list of this set's elements. The list is
477+
-- | Return a list of this set's elements. The list is
396478
-- produced lazily.
479+
--
480+
-- __Complexity:__ /O(n)/
397481
toList :: HashSet a -> [a]
398482
toList t = build (\ c z -> foldrWithKey ((const .) c) z (asMap t))
399483
{-# INLINE toList #-}
400484

401-
-- | /O(n*min(W, n))/ Construct a set from a list of elements.
485+
-- | Construct a set from a list of elements.
486+
--
487+
-- __Complexity:__ /O(n*min(W, n))/
402488
fromList :: (Eq a, Hashable a) => [a] -> HashSet a
403489
fromList = HashSet . List.foldl' (\ m k -> H.insert k () m) H.empty
404490
{-# INLINE fromList #-}

0 commit comments

Comments
 (0)