Skip to content

Commit 3dbdf8f

Browse files
authored
Add HashMap.lookupKey and HashSet.lookupElement (#554)
Resolves #546.
1 parent d06eb34 commit 3dbdf8f

File tree

7 files changed

+53
-0
lines changed

7 files changed

+53
-0
lines changed

Data/HashMap/Internal.hs

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@ module Data.HashMap.Internal
4747
, findWithDefault
4848
, lookupDefault
4949
, (!)
50+
, lookupKey
5051
, insert
5152
, insertWith
5253
, unsafeInsert
@@ -784,6 +785,40 @@ lookupDefault = findWithDefault
784785

785786
infixl 9 !
786787

788+
-- | \(O(\log n)\) For a given key, return the equal key stored in the map,
789+
-- if present, otherwise return 'Nothing'.
790+
--
791+
-- This function can be used for /interning/, i.e. to reduce memory usage.
792+
lookupKey :: Hashable k => k -> HashMap k v -> Maybe k
793+
lookupKey k = \m -> fromMaybe# (lookupKeyInSubtree# 0 (hash k) k m)
794+
where
795+
fromMaybe# (# (##) | #) = Nothing
796+
fromMaybe# (# | a #) = Just a
797+
{-# INLINE lookupKey #-}
798+
799+
lookupKeyInSubtree# :: Eq k => Shift -> Hash -> k -> HashMap k v -> (# (##) | k #)
800+
lookupKeyInSubtree# !s !hx kx = \case
801+
Empty -> (# (##) | #)
802+
Leaf hy (L ky _)
803+
| hx == hy && kx == ky -> (# | ky #)
804+
| otherwise -> (# (##) | #)
805+
BitmapIndexed b ary
806+
| m .&. b == 0 -> (# (##) | #)
807+
| otherwise -> case A.index# ary i of
808+
(# st #) -> lookupKeyInSubtree# (nextShift s) hx kx st
809+
where
810+
m = mask hx s
811+
i = sparseIndex b m
812+
Full ary -> case A.index# ary (index hx s) of
813+
(# st #) -> lookupKeyInSubtree# (nextShift s) hx kx st
814+
Collision hy ary
815+
| hx == hy
816+
, Just i <- indexOf kx ary
817+
, (# L ky _ #) <- A.index# ary i
818+
-> (# | ky #)
819+
| otherwise -> (# (##) | #)
820+
{-# INLINABLE lookupKeyInSubtree# #-}
821+
787822
-- | Create a 'Collision' value with two 'Leaf' values.
788823
collision :: Hash -> Leaf k v -> Leaf k v -> HashMap k v
789824
collision h !e1 !e2 =

Data/HashMap/Internal/Strict.hs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,7 @@ module Data.HashMap.Internal.Strict
6262
, HM.findWithDefault
6363
, HM.lookupDefault
6464
, (HM.!)
65+
, HM.lookupKey
6566
, insert
6667
, insertWith
6768
, HM.delete

Data/HashMap/Lazy.hs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ module Data.HashMap.Lazy
4242
, findWithDefault
4343
, lookupDefault
4444
, (!)
45+
, lookupKey
4546
, insert
4647
, insertWith
4748
, delete

Data/HashMap/Strict.hs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ module Data.HashMap.Strict
4141
, findWithDefault
4242
, lookupDefault
4343
, (!)
44+
, lookupKey
4445
, insert
4546
, insertWith
4647
, delete

Data/HashSet.hs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,7 @@ module Data.HashSet
107107
, null
108108
, size
109109
, member
110+
, lookupElement
110111
, insert
111112
, delete
112113
, isSubsetOf

Data/HashSet/Internal.hs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@ module Data.HashSet.Internal
5252
, null
5353
, size
5454
, member
55+
, lookupElement
5556
, insert
5657
, delete
5758
, isSubsetOf
@@ -350,6 +351,14 @@ member a s = case H.lookup a (asMap s) of
350351
_ -> False
351352
{-# INLINABLE member #-}
352353

354+
-- | \(O(\log n)\) For a given value, return the equal element in the set if
355+
-- present, otherwise return 'Nothing'.
356+
--
357+
-- This is useful for /interning/, i.e. to reduce memory usage.
358+
lookupElement :: Hashable a => a -> HashSet a -> Maybe a
359+
lookupElement a = H.lookupKey a . asMap
360+
{-# INLINE lookupElement #-}
361+
353362
-- | \(O(\log n)\) Add the specified value to this set.
354363
--
355364
-- >>> HashSet.insert 1 HashSet.empty

tests/Properties/HashMapLazy.hs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ import Data.Function (on)
2424
import Data.Functor.Identity (Identity (..))
2525
import Data.Hashable (Hashable (hashWithSalt))
2626
import Data.HashMap.Internal.Debug (Validity (..), valid)
27+
import Data.Maybe (isJust)
2728
import Data.Ord (comparing)
2829
import Test.QuickCheck (Arbitrary (..), Fun, Property, pattern Fn,
2930
pattern Fn2, pattern Fn3, (===), (==>))
@@ -173,6 +174,10 @@ tests =
173174
\(k :: Key) (m :: HMKI) -> HM.lookup k m === M.lookup k (toOrdMap m)
174175
, testProperty "!?" $
175176
\(k :: Key) (m :: HMKI) -> m HM.!? k === M.lookup k (toOrdMap m)
177+
, testGroup "lookupKey" $
178+
[ testProperty "isJust (lookupKey k m) == member k m" $
179+
\(k :: Key) (m :: HMKI) -> isJust (HM.lookupKey k m) === HM.member k m
180+
]
176181
, testGroup "insert"
177182
[ testProperty "model" $
178183
\(k :: Key) (v :: Int) x ->

0 commit comments

Comments
 (0)