Skip to content

Commit 9182f4f

Browse files
committed
Introduce ‘partitionKeys’ that fuses ‘restrictKeys’ and ‘withoutKeys’ in one go
1 parent 20244f5 commit 9182f4f

7 files changed

Lines changed: 68 additions & 3 deletions

File tree

containers-tests/benchmarks/Map.hs

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ import Data.Coerce
1818
import Data.Tuple.Solo (Solo (MkSolo), getSolo)
1919
import System.Random (StdGen, mkStdGen, random, randoms)
2020
import Prelude hiding (lookup)
21+
import Utils.Containers.Internal.Strict (StrictPair(..))
2122

2223
import Utils.Fold (foldBenchmarks, foldWithKeyBenchmarks)
2324
import Utils.Random (shuffle)
@@ -26,9 +27,10 @@ main = do
2627
let m = M.fromList elems :: M.Map Int Int
2728
m_even = M.fromList elems_even :: M.Map Int Int
2829
m_odd = M.fromList elems_odd :: M.Map Int Int
30+
s_odd_keys = M.keysSet m_odd :: Set.Set Int
2931
s_random = Set.fromList keys_random :: Set.Set Int
3032
evaluate $ rnf [m, m_even, m_odd]
31-
evaluate $ rnf [s_random]
33+
evaluate $ rnf [s_random, s_odd_keys]
3234
evaluate $ rnf
3335
[elems_distinct_asc, elems_distinct_desc, elems_asc, elems_desc]
3436
evaluate $ rnf [keys_random]
@@ -139,6 +141,7 @@ main = do
139141
, bench "Lazy.fromSetA inner" $ whnf (getSolo . M.fromSetA (MkSolo . pred)) s_random
140142
, bench "Strict.fromSetA inner" $ whnf (getSolo . MS.fromSetA (MkSolo . pred)) s_random
141143
, bench "minView" $ whnf (\m' -> case M.minViewWithKey m' of {Nothing -> 0; Just ((k,v),m'') -> k+v+M.size m''}) (M.fromAscList $ zip [1..10::Int] [100..110::Int])
144+
142145
, bench "eq" $ whnf (\m' -> m' == m') m -- worst case, compares everything
143146
, bench "compare" $ whnf (\m' -> compare m' m') m -- worst case, compares everything
144147
, bgroup "folds" $ foldBenchmarks M.foldr M.foldl M.foldr' M.foldl' foldMap m
@@ -148,6 +151,10 @@ main = do
148151
, bench "mapKeys:desc" $ whnf (M.mapKeys (negate . (+1))) m
149152
, bench "mapKeysWith:asc" $ whnf (M.mapKeysWith (+) (`div` 2)) m
150153
, bench "mapKeysWith:desc" $ whnf (M.mapKeysWith (+) (negate . (`div` 2))) m
154+
155+
, bench "restrictKeys" $ whnf (M.restrictKeys m) s_odd_keys
156+
, bench "withoutKeys" $ whnf (M.withoutKeys m) s_odd_keys
157+
, bench "partitionKeys" $ whnf (M.partitionKeys m) s_odd_keys
151158
]
152159
where
153160
bound = 2^14

containers-tests/containers-tests.cabal

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -121,12 +121,12 @@ library
121121
Data.Tree
122122
Utils.Containers.Internal.BitQueue
123123
Utils.Containers.Internal.BitUtil
124+
Utils.Containers.Internal.Strict
124125

125126
other-modules:
126127
Utils.Containers.Internal.Prelude
127128
Utils.Containers.Internal.PtrEquality
128129
Utils.Containers.Internal.State
129-
Utils.Containers.Internal.Strict
130130
Utils.Containers.Internal.EqOrdUtil
131131

132132
if impl(ghc >= 8.6)

containers-tests/tests/map-properties.hs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -181,6 +181,7 @@ main = defaultMain $ testGroup "map-properties"
181181
, testProperty "withoutKeys" prop_withoutKeys
182182
, testProperty "intersection" prop_intersection
183183
, testProperty "restrictKeys" prop_restrictKeys
184+
, testProperty "partitionKeys" prop_partitionKeys
184185
, testProperty "intersection model" prop_intersectionModel
185186
, testProperty "intersectionWith" prop_intersectionWith
186187
, testProperty "intersectionWithModel" prop_intersectionWithModel
@@ -1168,6 +1169,15 @@ prop_withoutKeys m s0 = valid reduced .&&. (m `withoutKeys` s === filterWithKey
11681169
s = keysSet s0
11691170
reduced = withoutKeys m s
11701171

1172+
prop_partitionKeys :: IMap -> IMap -> Property
1173+
prop_partitionKeys m s0 =
1174+
valid with .&&.
1175+
valid without .&&.
1176+
(m `partitionKeys` s === (m `restrictKeys` s, m `withoutKeys` s))
1177+
where
1178+
s = keysSet s0
1179+
(with, without) = partitionKeys m s
1180+
11711181
prop_intersection :: IMap -> IMap -> Bool
11721182
prop_intersection t1 t2 = valid (intersection t1 t2)
11731183

containers/src/Data/Map/Internal.hs

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
{-# LANGUAGE StandaloneDeriving #-}
88
{-# LANGUAGE Trustworthy #-}
99
{-# LANGUAGE TypeFamilies #-}
10+
{-# LANGUAGE ScopedTypeVariables #-}
1011
#define USE_MAGIC_PROXY 1
1112
#endif
1213

@@ -300,6 +301,7 @@ module Data.Map.Internal (
300301

301302
, restrictKeys
302303
, withoutKeys
304+
, partitionKeys
303305
, partition
304306
, partitionWithKey
305307

@@ -1933,6 +1935,48 @@ withoutKeys m (Set.Bin _ k ls rs) = case splitMember k m of
19331935
!rm' = withoutKeys rm rs
19341936
{-# INLINABLE withoutKeys #-}
19351937

1938+
-- | \(O\bigl(m \log\bigl(\frac{n}{m}+1\bigr)\bigr), \; 0 < m \leq n\). Partition the map according to a set.
1939+
-- The first map contains the input 'Map' restricted to those keys found in the 'Set',
1940+
-- the second map contains the input 'Map' without all keys in the 'Set'.
1941+
-- This is equivalent to using 'restrictKeys' and 'withoutKeys' together but is more efficient.
1942+
--
1943+
-- @
1944+
-- m \`partitionKeys\` s = (m ``restrictKeys`` s, m ``withoutKeys`` s)
1945+
-- @
1946+
partitionKeys :: forall k a. Ord k => Map k a -> Set k -> (Map k a, Map k a)
1947+
partitionKeys xs ys =
1948+
case go xs ys of
1949+
xs' :*: ys' -> (xs', ys')
1950+
where
1951+
go :: Map k a -> Set k -> StrictPair (Map k a) (Map k a)
1952+
go Tip _ = Tip :*: Tip
1953+
go m Set.Tip = Tip :*: m
1954+
go m@(Bin _ k x lm rm) s@Set.Bin{} =
1955+
case b of
1956+
True -> with :*: without
1957+
where
1958+
with =
1959+
if lmWith `ptrEq` lm && rmWith `ptrEq` rm
1960+
then m
1961+
else link k x lmWith rmWith
1962+
without =
1963+
link2 lmWithout rmWithout
1964+
False -> with :*: without
1965+
where
1966+
with = link2 lmWith rmWith
1967+
without =
1968+
if lmWithout `ptrEq` lm && rmWithout `ptrEq` rm
1969+
then m
1970+
else link k x lmWithout rmWithout
1971+
where
1972+
!(lmWith :*: lmWithout) = go lm ls'
1973+
!(rmWith :*: rmWithout) = go rm rs'
1974+
1975+
!(!ls', b, !rs') = Set.splitMember k s
1976+
#if __GLASGOW_HASKELL__
1977+
{-# INLINABLE partitionKeys #-}
1978+
#endif
1979+
19361980
-- | \(O(n+m)\). Difference with a combining function.
19371981
-- When two equal keys are
19381982
-- encountered, the combining function is applied to the values of these keys.

containers/src/Data/Map/Lazy.hs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -247,6 +247,7 @@ module Data.Map.Lazy (
247247
, filterWithKey
248248
, restrictKeys
249249
, withoutKeys
250+
, partitionKeys
250251
, partition
251252
, partitionWithKey
252253
, takeWhileAntitone

containers/src/Data/Map/Strict.hs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -261,6 +261,7 @@ module Data.Map.Strict
261261
, filterWithKey
262262
, restrictKeys
263263
, withoutKeys
264+
, partitionKeys
264265
, partition
265266
, partitionWithKey
266267

containers/src/Data/Map/Strict/Internal.hs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -242,6 +242,7 @@ module Data.Map.Strict.Internal
242242
, filterWithKey
243243
, restrictKeys
244244
, withoutKeys
245+
, partitionKeys
245246
, partition
246247
, partitionWithKey
247248
, takeWhileAntitone
@@ -409,7 +410,8 @@ import Data.Map.Internal
409410
, toDescList
410411
, union
411412
, unions
412-
, withoutKeys )
413+
, withoutKeys
414+
, partitionKeys )
413415

414416
import Data.Map.Internal.Debug (valid)
415417

0 commit comments

Comments
 (0)