Skip to content

Commit 646e922

Browse files
committed
Add Map curry/uncurry utils
1 parent 722218d commit 646e922

6 files changed

Lines changed: 96 additions & 5 deletions

File tree

containers-tests/benchmarks/Map.hs

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
{-# LANGUAGE BangPatterns #-}
33
module Main where
44

5-
import Control.Applicative (Const(Const, getConst), pure)
5+
import Control.Applicative (Const(Const, getConst), liftA2, pure)
66
import Control.DeepSeq (rnf)
77
import Control.Exception (evaluate)
88
import Test.Tasty.Bench (bench, bgroup, defaultMain, whnf, nf)
@@ -31,12 +31,16 @@ main = do
3131
let m = M.fromList elems :: M.Map Int Int
3232
m_even = M.fromList elems_even :: M.Map Int Int
3333
m_odd = M.fromList elems_odd :: M.Map Int Int
34+
m_uncurried = M.fromList elems_uncurried :: M.Map (Int, Int) Int
35+
m_curried = M.curry m_uncurried :: M.Map Int (M.Map Int Int)
3436
s_random = Set.fromList keys_random :: Set.Set Int
3537
evaluate $ rnf [m, m_even, m_odd]
3638
evaluate $ rnf [s_random]
3739
evaluate $ rnf
3840
[elems_distinct_asc, elems_distinct_desc, elems_asc, elems_desc]
3941
evaluate $ rnf [keys_random]
42+
evaluate $ rnf [m_uncurried]
43+
evaluate $ rnf [m_curried]
4044
defaultMain
4145
[ bench "lookup absent" $ whnf (lookup evens) m_odd
4246
, bench "lookup present" $ whnf (lookup evens) m_even
@@ -143,6 +147,8 @@ main = do
143147
, bench "Strict.fromSetA outer" $ whnf (MS.fromSetA (MkSolo . pred)) s_random
144148
, bench "Lazy.fromSetA inner" $ whnf (getSolo . M.fromSetA (MkSolo . pred)) s_random
145149
, bench "Strict.fromSetA inner" $ whnf (getSolo . MS.fromSetA (MkSolo . pred)) s_random
150+
, bench "curry" $ whnf M.curry m_uncurried
151+
, bench "uncurry" $ whnf M.uncurry m_curried
146152
, 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])
147153
, bench "eq" $ whnf (\m' -> m' == m') m -- worst case, compares everything
148154
, bench "compare" $ whnf (\m' -> compare m' m') m -- worst case, compares everything
@@ -155,7 +161,8 @@ main = do
155161
, bench "mapKeysWith:desc" $ whnf (M.mapKeysWith (+) (negate . (`div` 2))) m
156162
]
157163
where
158-
bound = 2^14
164+
magnitude = 14
165+
bound = 2^magnitude
159166
elems = shuffle elems_distinct_asc
160167
elems_even = zip evens evens
161168
elems_odd = zip odds odds
@@ -172,6 +179,11 @@ main = do
172179
sumkv k v1 v2 = k + v1 + v2
173180
consPair k v xs = (k, v) : xs
174181
keys_random = take bound (randoms gen)
182+
elems_uncurried = zip xs evens
183+
where
184+
left = magnitude `div` 2
185+
right = magnitude - left
186+
xs = shuffle $ liftA2 (,) [1..2^left] $ reverse [1..2^right]
175187

176188
add3 :: Int -> Int -> Int -> Int
177189
add3 x y z = x + y + z

containers-tests/tests/map-properties.hs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -278,6 +278,7 @@ main = defaultMain $ testGroup "map-properties"
278278
, testProperty "fromSetMaybe" prop_fromSetMaybe
279279
, testProperty "fromSetMaybeA" prop_fromSetMaybeA
280280
, testProperty "fromArgSet" prop_fromArgSet
281+
, testProperty "curry" prop_curry
281282
, testProperty "takeWhileAntitone" prop_takeWhileAntitone
282283
, testProperty "dropWhileAntitone" prop_dropWhileAntitone
283284
, testProperty "spanAntitone" prop_spanAntitone
@@ -2013,6 +2014,10 @@ prop_fromArgSet :: [(OrdA, B)] -> Property
20132014
prop_fromArgSet ys =
20142015
fromArgSet (Set.fromList $ List.map (uncurry Arg) ys) === fromList ys
20152016

2017+
prop_curry :: Map Int (Map Int A) -> Property
2018+
prop_curry m = m' === Data.Map.uncurry (Data.Map.curry m')
2019+
where m' = Data.Map.uncurry m
2020+
20162021
prop_eq :: Map Int A -> Map Int A -> Property
20172022
prop_eq m1 m2 = (m1 == m2) === (toList m1 == toList m2)
20182023

containers/src/Data/Map/Internal.hs

Lines changed: 34 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -268,6 +268,10 @@ module Data.Map.Internal (
268268
, fromSetMaybeA
269269
, fromArgSet
270270

271+
-- ** Maps
272+
, curry
273+
, uncurry
274+
271275
-- ** Lists
272276
, toList
273277
, fromList
@@ -391,7 +395,7 @@ import Control.DeepSeq (NFData(rnf),NFData1(liftRnf),NFData2(liftRnf2))
391395
import qualified Data.Foldable as Foldable
392396
import Data.Bifoldable
393397
import Utils.Containers.Internal.Prelude hiding
394-
(lookup, map, filter, foldr, foldl, foldl', null, splitAt, take, drop)
398+
(lookup, map, filter, foldr, foldl, foldl', null, splitAt, take, drop, curry, uncurry)
395399
import Prelude ()
396400

397401
import qualified Data.Set.Internal as Set
@@ -3427,6 +3431,35 @@ fromArgSet :: Set.Set (Arg k a) -> Map k a
34273431
fromArgSet Set.Tip = Tip
34283432
fromArgSet (Set.Bin sz (Arg x v) l r) = Bin sz x v (fromArgSet l) (fromArgSet r)
34293433

3434+
{--------------------------------------------------------------------
3435+
Maps
3436+
--------------------------------------------------------------------}
3437+
-- | \(O(n)\). Group map entries by the first component.
3438+
--
3439+
-- > curry $ fromList [((1,2),12),((1,3),13)] == fromList [(1,fromList [(2,12),(3,13)])]
3440+
--
3441+
-- @since FIXME
3442+
curry :: (Ord a, Ord b) => Map (a,b) c -> Map a (Map b c)
3443+
curry m = fmap (fromDescList . ($ [])) $ fromAscListWith (.) $ fmap (\((a,b),c) -> (a, ((b,c):))) $ toAscList m
3444+
3445+
-- | \(O(n)\). Flatten nested maps.
3446+
--
3447+
-- Note
3448+
--
3449+
-- > uncurry . curry = id
3450+
--
3451+
-- but not the other way around
3452+
--
3453+
-- > uncurry (fromList [(1, fromList [])]) == fromList []
3454+
-- > uncurry (fromList [(1, fromList [(2,12),(3,13)])]) == fromList [((1,2),12),((1,3),13)]
3455+
--
3456+
-- @since FIXME
3457+
uncurry :: (Ord a, Ord b) => Map a (Map b c) -> Map (a,b) c
3458+
uncurry m = fromAscList $ do
3459+
(a,b2c) <- toAscList m
3460+
(b,c) <- toAscList b2c
3461+
pure ((a,b), c)
3462+
34303463
{--------------------------------------------------------------------
34313464
Lists
34323465
--------------------------------------------------------------------}

containers/src/Data/Map/Lazy.hs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -240,6 +240,10 @@ module Data.Map.Lazy (
240240
, keysSet
241241
, argSet
242242

243+
-- ** Maps
244+
, curry
245+
, uncurry
246+
243247
-- ** Lists
244248
, toList
245249

containers/src/Data/Map/Strict.hs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -254,6 +254,10 @@ module Data.Map.Strict
254254
, keysSet
255255
, argSet
256256

257+
-- ** Maps
258+
, curry
259+
, uncurry
260+
257261
-- ** Lists
258262
, toList
259263

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

Lines changed: 35 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -219,6 +219,10 @@ module Data.Map.Strict.Internal
219219
, fromSetMaybeA
220220
, fromArgSet
221221

222+
-- ** Maps
223+
, curry
224+
, uncurry
225+
222226
-- ** Lists
223227
, toList
224228
, fromList
@@ -298,7 +302,7 @@ module Data.Map.Strict.Internal
298302
) where
299303

300304
import Utils.Containers.Internal.Prelude hiding
301-
(lookup,map,filter,foldr,foldl,foldl',null,take,drop,splitAt)
305+
(lookup,map,filter,foldr,foldl,foldl',null,take,drop,splitAt,curry,uncurry)
302306
import Prelude ()
303307

304308
import Data.Map.Internal
@@ -1427,7 +1431,7 @@ fromSet f = runIdentity . fromSetA (pure . f)
14271431
-- @since FIXME
14281432
fromSetA :: Applicative f => (k -> f a) -> Set k -> f (Map k a)
14291433
fromSetA _ Set.Tip = pure Tip
1430-
fromSetA f (Set.Bin sz x l r) =
1434+
fromSetA f (Set.Bin sz x l r) =
14311435
liftA3 (flip (Bin sz x $!)) (fromSetA f l) (f x) (fromSetA f r)
14321436
{-# INLINABLE fromSetA #-}
14331437

@@ -1471,6 +1475,35 @@ fromArgSet :: Set.Set (Arg k a) -> Map k a
14711475
fromArgSet Set.Tip = Tip
14721476
fromArgSet (Set.Bin sz (Arg x v) l r) = v `seq` Bin sz x v (fromArgSet l) (fromArgSet r)
14731477

1478+
{--------------------------------------------------------------------
1479+
Maps
1480+
--------------------------------------------------------------------}
1481+
-- | \(O(n)\). Group map entries by the first component.
1482+
--
1483+
-- > curry $ fromList [((1,2),12),((1,3),13)] == fromList [(1,fromList [(2,12),(3,13)])]
1484+
--
1485+
-- @since FIXME
1486+
curry :: (Ord a, Ord b) => Map (a,b) c -> Map a (Map b c)
1487+
curry m = fmap (fromDescList . ($ [])) $ fromAscListWith (.) $ fmap (\((a,b),c) -> (a, ((b,c):))) $ toAscList m
1488+
1489+
-- | \(O(n)\). Flatten nested maps.
1490+
--
1491+
-- Note
1492+
--
1493+
-- > uncurry . curry = id
1494+
--
1495+
-- but not the other way around
1496+
--
1497+
-- > uncurry (fromList [(1, fromList [])]) == fromList []
1498+
-- > uncurry (fromList [(1, fromList [(2,12),(3,13)])]) == fromList [((1,2),12),((1,3),13)]
1499+
--
1500+
-- @since FIXME
1501+
uncurry :: (Ord a, Ord b) => Map a (Map b c) -> Map (a,b) c
1502+
uncurry m = fromAscList $ do
1503+
(a,b2c) <- toAscList m
1504+
(b,c) <- toAscList b2c
1505+
pure ((a,b), c)
1506+
14741507
{--------------------------------------------------------------------
14751508
Lists
14761509
--------------------------------------------------------------------}

0 commit comments

Comments
 (0)