Skip to content

Commit f612b0d

Browse files
committed
Add size-aware 'filter, mapMaybe' functions
1 parent 2357b66 commit f612b0d

File tree

1 file changed

+113
-0
lines changed

1 file changed

+113
-0
lines changed

Data/HashMap/Base.hs

Lines changed: 113 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1600,12 +1600,35 @@ mapMaybeWithKey f = filterMapAux onLeaf onColl
16001600
| otherwise = Nothing
16011601
{-# INLINE mapMaybeWithKey #-}
16021602

1603+
-- | /O(n)/ Transform this map by applying a function to every value
1604+
-- and retaining only some of them.
1605+
-- Returns a tuple with the size of the result hashmap and the the hashmap
1606+
-- itself.
1607+
mapMaybeWithKeyInternal
1608+
:: (k -> v1 -> Maybe v2)
1609+
-> HashMap k v1
1610+
-> (Int, HashMap k v2)
1611+
mapMaybeWithKeyInternal f = filterMapAuxInternal onLeaf onColl
1612+
where onLeaf (Leaf h (L k v)) | Just v' <- f k v = Just (Leaf h (L k v'))
1613+
onLeaf _ = Nothing
1614+
1615+
onColl (L k v) | Just v' <- f k v = Just (L k v')
1616+
| otherwise = Nothing
1617+
{-# INLINE mapMaybeWithKeyInternal #-}
1618+
16031619
-- | /O(n)/ Transform this map by applying a function to every value
16041620
-- and retaining only some of them.
16051621
mapMaybe :: (v1 -> Maybe v2) -> HashMap k v1 -> HashMap k v2
16061622
mapMaybe f = mapMaybeWithKey (const f)
16071623
{-# INLINE mapMaybe #-}
16081624

1625+
-- | /O(n)/ Transform this map by applying a function to every value
1626+
-- and retaining only some of them.
1627+
-- Returns a tuple with the result hashmap's size and the hasmap itself.
1628+
mapMaybeInternal :: (v1 -> Maybe v2) -> HashMap k v1 -> (Int, HashMap k v2)
1629+
mapMaybeInternal f = mapMaybeWithKeyInternal (const f)
1630+
{-# INLINE mapMaybeInternal #-}
1631+
16091632
-- | /O(n)/ Filter this map by retaining only elements satisfying a
16101633
-- predicate.
16111634
filterWithKey :: forall k v. (k -> v -> Bool) -> HashMap k v -> HashMap k v
@@ -1617,6 +1640,17 @@ filterWithKey pred = filterMapAux onLeaf onColl
16171640
onColl _ = Nothing
16181641
{-# INLINE filterWithKey #-}
16191642

1643+
-- | /O(n)/ Filter this map by retaining only elements satisfying a
1644+
-- predicate.
1645+
-- Returns a tuple with the result hashmap's size and the hashmap itself.
1646+
filterWithKeyInternal :: forall k v. (k -> v -> Bool) -> HashMap k v -> (Int, HashMap k v)
1647+
filterWithKeyInternal pred = filterMapAuxInternal onLeaf onColl
1648+
where onLeaf t@(Leaf _ (L k v)) | pred k v = Just t
1649+
onLeaf _ = Nothing
1650+
1651+
onColl el@(L k v) | pred k v = Just el
1652+
onColl _ = Nothing
1653+
{-# INLINE filterWithKeyInternal #-}
16201654

16211655
-- | Common implementation for 'filterWithKey' and 'mapMaybeWithKey',
16221656
-- allowing the former to former to reuse terms.
@@ -1687,12 +1721,91 @@ filterMapAux onLeaf onColl = go
16871721
| otherwise = step ary mary (i+1) j n
16881722
{-# INLINE filterMapAux #-}
16891723

1724+
-- | Common implementation for 'filterWithKey' and 'mapMaybeWithKey',
1725+
-- allowing the former to former to reuse terms.
1726+
-- Returns the change in the hashmap's size, and the hashmap itself.
1727+
filterMapAuxInternal :: forall k v1 v2
1728+
. (HashMap k v1 -> Maybe (HashMap k v2))
1729+
-> (Leaf k v1 -> Maybe (Leaf k v2))
1730+
-> HashMap k v1
1731+
-> (Int, HashMap k v2)
1732+
filterMapAuxInternal onLeaf onColl = go 0
1733+
where
1734+
go !sz Empty = (sz, Empty)
1735+
go !sz t@Leaf{}
1736+
| Just t' <- onLeaf t = (sz + 1, t')
1737+
| otherwise = (sz, Empty)
1738+
go !sz (BitmapIndexed b ary) = filterA sz ary b
1739+
go !sz (Full ary) = filterA sz ary fullNodeMask
1740+
go !sz (Collision h ary) = filterC sz ary h
1741+
1742+
filterA size ary0 b0 =
1743+
let !n = A.length ary0
1744+
in runST $ do
1745+
mary <- A.new_ n
1746+
step ary0 mary b0 0 0 1 n size
1747+
where
1748+
step :: A.Array (HashMap k v1) -> A.MArray s (HashMap k v2)
1749+
-> Bitmap -> Int -> Int -> Bitmap -> Int -> Int
1750+
-> ST s (Int, HashMap k v2)
1751+
step !ary !mary !b i !j !bi n !sz
1752+
| i >= n = case j of
1753+
0 -> return (sz, Empty)
1754+
1 -> do
1755+
ch <- A.read mary 0
1756+
case ch of
1757+
t | isLeafOrCollision t -> return (sz, t)
1758+
_ -> (sz,) . BitmapIndexed b <$> trim mary 1
1759+
_ -> do
1760+
ary2 <- trim mary j
1761+
return $! (sz,) (if j == maxChildren
1762+
then Full ary2
1763+
else BitmapIndexed b ary2)
1764+
| bi .&. b == 0 = step ary mary b i j (bi `unsafeShiftL` 1) n sz
1765+
| otherwise = case go sz (A.index ary i) of
1766+
(dsz, Empty) -> step ary mary (b .&. complement bi) (i+1) j
1767+
(bi `unsafeShiftL` 1) n dsz
1768+
(dsz, t) -> do A.write mary j t
1769+
step ary mary b (i+1) (j+1)
1770+
(bi `unsafeShiftL` 1) n dsz
1771+
1772+
filterC size ary0 h =
1773+
let !n = A.length ary0
1774+
in runST $ do
1775+
mary <- A.new_ n
1776+
step ary0 mary 0 0 n size
1777+
where
1778+
step :: A.Array (Leaf k v1) -> A.MArray s (Leaf k v2)
1779+
-> Int -> Int -> Int -> Int
1780+
-> ST s (Int, HashMap k v2)
1781+
step !ary !mary i !j n !sz
1782+
| i >= n = case j of
1783+
0 -> return (sz, Empty)
1784+
1 -> do l <- A.read mary 0
1785+
return $! (sz, Leaf h l)
1786+
_ | i == j -> do ary2 <- A.unsafeFreeze mary
1787+
return $! (sz, Collision h ary2)
1788+
| otherwise -> do ary2 <- trim mary j
1789+
return $! (sz, Collision h ary2)
1790+
| Just el <- onColl (A.index ary i)
1791+
= A.write mary j el >> step ary mary (i+1) (j+1) n (sz + 1)
1792+
| otherwise = step ary mary (i+1) j n sz
1793+
{-# INLINE filterMapAuxInternal #-}
1794+
16901795
-- | /O(n)/ Filter this map by retaining only elements which values
16911796
-- satisfy a predicate.
16921797
filter :: (v -> Bool) -> HashMap k v -> HashMap k v
16931798
filter p = filterWithKey (\_ v -> p v)
16941799
{-# INLINE filter #-}
16951800

1801+
-- | /O(n)/ Filter this map by retaining only elements which values
1802+
-- satisfy a predicate.
1803+
-- Returns a tuple with the new size of the result hashmap, and the hashmap
1804+
-- itself.
1805+
filterInternal :: (v -> Bool) -> HashMap k v -> (Int, HashMap k v)
1806+
filterInternal p = filterWithKeyInternal (\_ v -> p v)
1807+
{-# INLINE filterInternal #-}
1808+
16961809
------------------------------------------------------------------------
16971810
-- * Conversions
16981811

0 commit comments

Comments
 (0)