git.iko.soy/typeable/ordered-containers
on master help sign in
should have set up version control ages ago
commita5ce35f597b4327a5214292fe3fb32ae93d89480 authorDaniel Wagner <me@dmwit.com> dateMon 26 Dec 2016, 00:51 (10 years ago)
7 files changed+366
ChangeLog.md+11 Data/Map/Ordered.hs+126 Data/Map/Util.hs+31 Data/Set/Ordered.hs+141 LICENSE+30 Setup.hs+2 ordered-containers.cabal+25
ChangeLog.mdnew file+11
@@ -0,0 +1,11 @@
1+# Revision history for ordered-containers
2+
3+## 0.1.0 -- 2016-12-26
4+
5+* Documentation fix
6+* Live up to the package version boundary claims
7+* Use enough version parts to conform to the PVP
8+
9+## 0.0 -- 2016-12-23
10+
11+* First version released on an unsuspecting world
Data/Map/Ordered.hsnew file+126
@@ -0,0 +1,126 @@
1+-- | An 'OMap' behaves much like a 'Map', with all the same asymptotics, but
2+-- also remembers the order that keys were inserted.
3+module Data.Map.Ordered
4+ ( OMap
5+ -- * Trivial maps
6+ , empty, singleton
7+ -- * Insertion
8+ -- | Conventions:
9+ --
10+ -- * The open side of an angle bracket points to an 'OMap'
11+ --
12+ -- * The pipe appears on the side whose indices take precedence if both sides contain the same key
13+ --
14+ -- * The left argument's indices are lower than the right argument's indices
15+ --
16+ -- * If both sides contain the same key, the tuple's value wins
17+ , (<|), (|<), (>|), (|>)
18+ -- * Deletion
19+ , delete, filter, (\\)
20+ -- * Query
21+ , null, size, member, notMember, lookup
22+ -- * Indexing
23+ , Index, findIndex, elemAt
24+ -- * List conversions
25+ , fromList, assocs, toAscList
26+ ) where
27+
28+import Control.Applicative ((<|>))
29+import Control.Monad (guard)
30+import Data.Foldable (Foldable, foldl', foldMap)
31+import Data.Function (on)
32+import Data.Map (Map)
33+import Data.Map.Util (Index, Tag, maxTag, minTag, nextHigherTag, nextLowerTag, readsPrecList, showsPrecList)
34+import Prelude hiding (filter, lookup, null)
35+import qualified Data.Map as M
36+
37+data OMap k v = OMap !(Map k (Tag, v)) !(Map Tag (k, v))
38+
39+-- | Values are produced in insertion order, not key order.
40+instance Foldable (OMap k) where foldMap f (OMap _ kvs) = foldMap (f . snd) kvs
41+instance ( Eq k, Eq v) => Eq (OMap k v) where (==) = (==) `on` assocs
42+instance ( Ord k, Ord v) => Ord (OMap k v) where compare = compare `on` assocs
43+instance ( Show k, Show v) => Show (OMap k v) where showsPrec = showsPrecList assocs
44+instance (Ord k, Read k, Read v) => Read (OMap k v) where readsPrec = readsPrecList fromList
45+
46+infixr 5 <|, |< -- copy :
47+infixl 5 >|, |>
48+
49+(<|) , (|<) :: Ord k => (,) k v -> OMap k v -> OMap k v
50+(>|) , (|>) :: Ord k => OMap k v -> (,) k v -> OMap k v
51+
52+(k, v) <| OMap tvs kvs = OMap (M.insert k (t, v) tvs) (M.insert t (k, v) kvs) where
53+ t = maybe (nextLowerTag kvs) fst (M.lookup k tvs)
54+
55+(k, v) |< o = OMap (M.insert k (t, v) tvs) (M.insert t (k, v) kvs) where
56+ t = nextLowerTag kvs
57+ OMap tvs kvs = delete k o
58+
59+o >| (k, v) = OMap (M.insert k (t, v) tvs) (M.insert t (k, v) kvs) where
60+ t = nextHigherTag kvs
61+ OMap tvs kvs = delete k o
62+
63+OMap tvs kvs |> (k, v) = OMap (M.insert k (t, v) tvs) (M.insert t (k, v) kvs) where
64+ t = maybe (nextHigherTag kvs) fst (M.lookup k tvs)
65+
66+-- | @m \\\\ n@ deletes all the keys that exist in @n@ from @m@
67+(\\) :: Ord k => OMap k v -> OMap k v' -> OMap k v
68+o@(OMap tvs kvs) \\ o'@(OMap tvs' kvs') = if size o < size o'
69+ then filter (const . (`notMember` o')) o
70+ else foldr delete o (fmap fst (assocs o'))
71+
72+empty :: OMap k v
73+empty = OMap M.empty M.empty
74+
75+singleton :: (k, v) -> OMap k v
76+singleton kv@(k, v) = OMap (M.singleton k (0, v)) (M.singleton 0 kv)
77+
78+-- | If a key appears multiple times, the first occurrence is used for ordering
79+-- and the last occurrence is used for its value. The library author welcomes
80+-- comments on whether this default is sane.
81+fromList :: Ord k => [(k, v)] -> OMap k v
82+fromList = foldl' (|>) empty
83+
84+null :: OMap k v -> Bool
85+null (OMap tvs _) = M.null tvs
86+
87+size :: OMap k v -> Int
88+size (OMap tvs _) = M.size tvs
89+
90+member, notMember :: Ord k => k -> OMap k v -> Bool
91+member k (OMap tvs _) = M.member k tvs
92+notMember k (OMap tvs _) = M.notMember k tvs
93+
94+lookup :: Ord k => k -> OMap k v -> Maybe v
95+lookup k (OMap tvs _) = fmap snd (M.lookup k tvs)
96+
97+-- The Ord constraint is for compatibility with older (<0.5) versions of
98+-- containers.
99+-- | @filter f m@ contains exactly the key-value pairs of @m@ that satisfy @f@,
100+-- without changing the order they appear
101+filter :: Ord k => (k -> v -> Bool) -> OMap k v -> OMap k v
102+filter f (OMap tvs kvs) = OMap (M.filterWithKey (\k (t, v) -> f k v) tvs)
103+ (M.filterWithKey (\t (k, v) -> f k v) kvs)
104+
105+delete :: Ord k => k -> OMap k v -> OMap k v
106+delete k o@(OMap tvs kvs) = case M.lookup k tvs of
107+ Nothing -> o
108+ Just (t, _) -> OMap (M.delete k tvs) (M.delete t kvs)
109+
110+findIndex :: Ord k => k -> OMap k v -> Maybe Index
111+findIndex k o@(OMap tvs kvs) = do
112+ (t, _) <- M.lookup k tvs
113+ M.lookupIndex t kvs
114+
115+elemAt :: OMap k v -> Index -> Maybe (k, v)
116+elemAt o@(OMap tvs kvs) i = do
117+ guard (0 <= i && i < M.size kvs)
118+ return . snd $ M.elemAt i kvs
119+
120+-- | Return key-value pairs in the order they were inserted.
121+assocs :: OMap k v -> [(k, v)]
122+assocs (OMap _ kvs) = map snd $ M.toAscList kvs
123+
124+-- | Return key-value pairs in order of increasing key.
125+toAscList :: OMap k v -> [(k, v)]
126+toAscList (OMap tvs kvs) = map (\(k, (t, v)) -> (k, v)) $ M.toAscList tvs
Data/Map/Util.hsnew file+31
@@ -0,0 +1,31 @@
1+module Data.Map.Util where
2+
3+import Data.Map (Map)
4+import qualified Data.Map as M
5+
6+-- | An internal index used to track ordering only -- its magnitude doesn't
7+-- matter. If you manage to see this documentation, the library author has made
8+-- a mistake!
9+type Tag = Int
10+
11+-- | A 0-based index, much like the indices used by lists' '!!' operation. All
12+-- indices are with respect to insertion order.
13+type Index = Int
14+
15+nextLowerTag, nextHigherTag :: Map Tag a -> Tag
16+nextLowerTag = maybe 0 pred . minTag
17+nextHigherTag = maybe 0 succ . maxTag
18+
19+minTag, maxTag :: Map Tag a -> Maybe Tag
20+minTag m = fmap (fst . fst) (M.minViewWithKey m)
21+maxTag m = fmap (fst . fst) (M.maxViewWithKey m)
22+
23+showsPrecList :: Show a => (b -> [a]) -> Int -> b -> ShowS
24+showsPrecList toList d o = showParen (d > 10) $
25+ showString "fromList " . shows (toList o)
26+
27+readsPrecList :: Read a => ([a] -> b) -> Int -> ReadS b
28+readsPrecList fromList d = readParen (d > 10) $ \r -> do
29+ ("fromList", s) <- lex r
30+ (xs, t) <- reads s
31+ return (fromList xs, t)
Data/Set/Ordered.hsnew file+141
@@ -0,0 +1,141 @@
1+-- | An 'OSet' behaves much like a 'Set', with all the same asymptotics, but
2+-- also remembers the order that values were inserted.
3+module Data.Set.Ordered
4+ ( OSet
5+ -- * Trivial sets
6+ , empty, singleton
7+ -- * Insertion
8+ -- | Conventionts:
9+ --
10+ -- * The open side of an angle bracket points to an 'OSet'
11+ --
12+ -- * The pipe appears on the side whose indices take precedence for keys that appear on both sides
13+ --
14+ -- * The left argument's indices are lower than the right argument's indices
15+ , (<|), (|<), (>|), (|>)
16+ , (<>|), (|<>)
17+ -- * Query
18+ , null, size, member, notMember
19+ -- * Deletion
20+ , delete, filter, (\\)
21+ -- * Indexing
22+ , Index, findIndex, elemAt
23+ -- * List conversions
24+ , fromList, toAscList
25+ ) where
26+
27+import Control.Monad (guard)
28+import Data.Foldable (Foldable, foldl', foldMap, foldr, toList)
29+import Data.Function (on)
30+import Data.Map (Map)
31+import Data.Map.Util (Index, Tag, maxTag, minTag, nextHigherTag, nextLowerTag, readsPrecList, showsPrecList)
32+import Data.Set (Set) -- so the haddocks link to the right place
33+import Prelude hiding (filter, foldr, lookup, null)
34+import qualified Data.Map as M
35+
36+data OSet a = OSet !(Map a Tag) !(Map Tag a)
37+
38+-- | Values appear in insertion order, not ascending order.
39+instance Foldable OSet where foldMap f (OSet _ vs) = foldMap f vs
40+instance Eq a => Eq (OSet a) where (==) = (==) `on` toList
41+instance Ord a => Ord (OSet a) where compare = compare `on` toList
42+instance Show a => Show (OSet a) where showsPrec = showsPrecList toList
43+instance (Ord a, Read a) => Read (OSet a) where readsPrec = readsPrecList fromList
44+
45+infixr 5 <|, |< -- copy :
46+infixl 5 >|, |>
47+infixr 6 <>|, |<> -- copy <>
48+
49+(<|) , (|<) :: Ord a => a -> OSet a -> OSet a
50+(>|) , (|>) :: Ord a => OSet a -> a -> OSet a
51+(<>|), (|<>) :: Ord a => OSet a -> OSet a -> OSet a
52+
53+v <| o@(OSet ts vs)
54+ | v `member` o = o
55+ | otherwise = OSet (M.insert v t ts) (M.insert t v vs) where
56+ t = nextLowerTag vs
57+
58+v |< o = OSet (M.insert v t ts) (M.insert t v vs) where
59+ t = nextLowerTag vs
60+ OSet ts vs = delete v o
61+
62+o@(OSet ts vs) |> v
63+ | v `member` o = o
64+ | otherwise = OSet (M.insert v t ts) (M.insert t v vs) where
65+ t = nextHigherTag vs
66+
67+o >| v = OSet (M.insert v t ts) (M.insert t v vs) where
68+ t = nextHigherTag vs
69+ OSet ts vs = delete v o
70+
71+o <>| o' = unsafeMappend (o \\ o') o'
72+o |<> o' = unsafeMappend o (o' \\ o)
73+
74+-- assumes that ts and ts' have disjoint keys
75+unsafeMappend (OSet ts vs) (OSet ts' vs')
76+ = OSet (M.union tsBumped tsBumped')
77+ (M.union vsBumped vsBumped')
78+ where
79+ bump = case maxTag vs of
80+ Nothing -> 0
81+ Just k -> -k-1
82+ bump' = case minTag vs' of
83+ Nothing -> 0
84+ Just k -> -k
85+ tsBumped = fmap (bump +) ts
86+ tsBumped' = fmap (bump'+) ts'
87+ vsBumped = (bump +) `M.mapKeysMonotonic` vs
88+ vsBumped' = (bump'+) `M.mapKeysMonotonic` vs'
89+
90+-- | Set difference: @r \\\\ s@ deletes all the values in @s@ from @r@. The
91+-- order of @r@ is unchanged.
92+(\\) :: Ord a => OSet a -> OSet a -> OSet a
93+o@(OSet ts vs) \\ o'@(OSet ts' vs') = if size o < size o'
94+ then filter (`notMember` o') o
95+ else foldr delete o vs'
96+
97+empty :: OSet a
98+empty = OSet M.empty M.empty
99+
100+member, notMember :: Ord a => a -> OSet a -> Bool
101+member v (OSet ts _) = M.member v ts
102+notMember v (OSet ts _) = M.notMember v ts
103+
104+size :: OSet a -> Int
105+size (OSet ts _) = M.size ts
106+
107+-- the Ord constraint is for compatibility with older (<0.5) versions of
108+-- containers
109+filter :: Ord a => (a -> Bool) -> OSet a -> OSet a
110+filter f (OSet ts vs) = OSet (M.filterWithKey (\v t -> f v) ts)
111+ (M.filterWithKey (\t v -> f v) vs)
112+
113+delete :: Ord a => a -> OSet a -> OSet a
114+delete v o@(OSet ts vs) = case M.lookup v ts of
115+ Nothing -> o
116+ Just t -> OSet (M.delete v ts) (M.delete t vs)
117+
118+singleton :: a -> OSet a
119+singleton v = OSet (M.singleton v 0) (M.singleton 0 v)
120+
121+-- | If a value occurs multiple times, only the first occurrence is used.
122+fromList :: Ord a => [a] -> OSet a
123+fromList = foldl' (|>) empty
124+
125+null :: OSet a -> Bool
126+null (OSet ts _) = M.null ts
127+
128+findIndex :: Ord a => a -> OSet a -> Maybe Index
129+findIndex v o@(OSet ts vs) = do
130+ t <- M.lookup v ts
131+ M.lookupIndex t vs
132+
133+elemAt :: OSet a -> Index -> Maybe a
134+elemAt o@(OSet ts vs) i = do
135+ guard (0 <= i && i < M.size vs)
136+ return . snd $ M.elemAt i vs
137+
138+-- | Returns values in ascending order. (Use 'toList' to return them in
139+-- insertion order.)
140+toAscList :: OSet a -> [a]
141+toAscList o@(OSet ts _) = fmap fst (M.toAscList ts)
LICENSEnew file+30
@@ -0,0 +1,30 @@
1+Copyright (c) 2016, Daniel Wagner
2+
3+All rights reserved.
4+
5+Redistribution and use in source and binary forms, with or without
6+modification, are permitted provided that the following conditions are met:
7+
8+ * Redistributions of source code must retain the above copyright
9+ notice, this list of conditions and the following disclaimer.
10+
11+ * Redistributions in binary form must reproduce the above
12+ copyright notice, this list of conditions and the following
13+ disclaimer in the documentation and/or other materials provided
14+ with the distribution.
15+
16+ * Neither the name of Daniel Wagner nor the names of other
17+ contributors may be used to endorse or promote products derived
18+ from this software without specific prior written permission.
19+
20+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
21+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
22+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
23+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
24+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
25+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
26+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
27+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
28+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
29+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
30+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
Setup.hsnew file+2
@@ -0,0 +1,2 @@
1+import Distribution.Simple
2+main = defaultMain
ordered-containers.cabalnew file+25
@@ -0,0 +1,25 @@
1+-- Initial ordered-containers.cabal generated by cabal init. For further
2+-- documentation, see http://haskell.org/cabal/users-guide/
3+
4+name: ordered-containers
5+version: 0.1.0
6+synopsis: Set- and Map-like types that remember the order elements were inserted
7+-- description:
8+license: BSD3
9+license-file: LICENSE
10+author: Daniel Wagner
11+maintainer: me@dmwit.com
12+-- copyright:
13+category: Data
14+build-type: Simple
15+extra-source-files: ChangeLog.md
16+cabal-version: >=1.10
17+
18+library
19+ exposed-modules: Data.Map.Ordered, Data.Set.Ordered
20+ other-modules: Data.Map.Util
21+ -- other-extensions:
22+ build-depends: base >=4 && <5, containers >=0.1 && <0.6
23+ -- hs-source-dirs:
24+ default-language: Haskell98
25+ ghc-options: -fno-warn-tabs