From bf84c7d167c2ba42170fa1e386315a4dbc47a3cb Mon Sep 17 00:00:00 2001 From: Veronika Romashkina Date: Thu, 18 Mar 2021 13:14:50 +0000 Subject: [PATCH] [#35] Integration with `containers` (#52) * [#35] Integration with `containers` Resolves #35 * Fix containers lower bound for GHC-8.2 --- CHANGELOG.md | 7 +++- slist.cabal | 2 ++ src/Slist.hs | 36 +++++++++++++++++++ src/Slist/Containers.hs | 79 +++++++++++++++++++++++++++++++++++++++++ src/Slist/Maybe.hs | 14 +++++++- 5 files changed, 136 insertions(+), 2 deletions(-) create mode 100644 src/Slist/Containers.hs diff --git a/CHANGELOG.md b/CHANGELOG.md index 282ad68..76aa15d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,7 +10,12 @@ The changelog is available [on GitHub][2]. * [#36](https://github.com/kowainik/slist/issues/36): Add strict functions: `append'`, `concat'` and `concatMap'`. * [#30](https://github.com/kowainik/slist/issues/30): - Add the `cons` function. + Add `cons` and `cons'` functions. +* [#35](https://github.com/kowainik/slist/issues/35): + Add integration with the `containers` library: `mapToKeys`, `mapToVals`, + `mapToPairs`, `setToSlist`. + + Add `ordNub`. * [#34](https://github.com/kowainik/slist/issues/34): Add `partitionWith` and `listPartitionWith`. * [#29](https://github.com/kowainik/slist/issues/29): diff --git a/slist.cabal b/slist.cabal index e886d8b..d4b1acd 100644 --- a/slist.cabal +++ b/slist.cabal @@ -62,8 +62,10 @@ common common-options library import: common-options + build-depends: containers >= 0.5 && <= 0.7 hs-source-dirs: src exposed-modules: Slist + Slist.Containers Slist.Maybe Slist.Size Slist.Type diff --git a/src/Slist.hs b/src/Slist.hs index 15f9046..fc156eb 100644 --- a/src/Slist.hs +++ b/src/Slist.hs @@ -191,6 +191,7 @@ module Slist -- $sets , nub , nubBy + , ordNub , delete , deleteBy , deleteFirstsBy @@ -223,6 +224,16 @@ module Slist , catMaybes , mapMaybe , slistWith + + -- * Containers + -- ** Map + , mapToVals + , mapToKeys + , mapToPairs + + -- ** Set + , setToSlist + ) where import Data.Bifunctor (bimap, first, second) @@ -237,11 +248,13 @@ import Prelude hiding (break, concat, concatMap, cycle, drop, dropWhile, filter, scanr1, span, splitAt, tail, take, takeWhile, unzip, unzip3, zip, zip3, zipWith, zipWith3) +import Slist.Containers (mapToKeys, mapToPairs, mapToVals, setToSlist) import Slist.Maybe (catMaybes, mapMaybe, maybeToSlist, slistToMaybe, slistWith) import Slist.Size (Size (..), sizes) import Slist.Type (Slist (..), cons, infiniteSlist, isEmpty, len, map, one, size, slist) import qualified Data.List as L +import qualified Data.Set as Set import qualified GHC.Exts as Exts import qualified Prelude as P @@ -1668,6 +1681,29 @@ nubBy f Slist{..} = let (s, l) = go 0 [] sList in case sSize of else go (n + 1) (res ++ [x]) xs {-# INLINE nubBy #-} +{- | Removes duplicate elements from a slist, keeping only the first occurance of +the element. + +Like 'nub' but runs in \( O(n \log n) \) time and requires 'Ord'. + +>>> ordNub $ slist [3, 3, 3, 2, 2, -1, 1] +Slist {sList = [3,2,-1,1], sSize = Size 4} + +-} +ordNub :: forall a . (Ord a) => Slist a -> Slist a +ordNub sl = let (s, l) = go 0 Set.empty (sList sl) in Slist + { sList = l + , sSize = Size s + } + where + go :: Int -> Set.Set a -> [a] -> (Int, [a]) + go !i _ [] = (i, []) + go i s (x:xs) = + if x `Set.member` s + then go i s xs + else second (x:) $ go (i + 1) (Set.insert x s) xs +{-# INLINEABLE ordNub #-} + {- | @O(n)@. Removes the first occurrence of the given element from its slist argument. diff --git a/src/Slist/Containers.hs b/src/Slist/Containers.hs new file mode 100644 index 0000000..5b96b16 --- /dev/null +++ b/src/Slist/Containers.hs @@ -0,0 +1,79 @@ +{- | +Copyright: (c) 2021 Kowainik +SPDX-License-Identifier: MPL-2.0 +Maintainer: Kowainik +Stability: Stable +Portability: Portable + +Useful combinators to work with the data structures from @containers@ package +and 'Slist' together. + +@since x.x.x.x +-} +module Slist.Containers + ( -- * Map + mapToVals + , mapToKeys + , mapToPairs + + -- * Set + , setToSlist + ) where + +import Data.Map.Strict (Map) +import Data.Set (Set) + +import Slist.Size (Size (..)) +import Slist.Type (Slist (..)) + +import qualified Data.Map.Strict as Map +import qualified Data.Set as Set + + +{- | @O(n)@. +Returns a 'Slist' of all values of the map in the ascending order of their keys. + +@since x.x.x.x +-} +mapToVals :: Map k v -> Slist v +mapToVals m = Slist + { sList = Map.elems m + , sSize = Size $ Map.size m + } +{-# INLINE mapToVals #-} + +{- | @O(n)@. +Returns a 'Slist' of all keys of the map in the ascending order. + +@since x.x.x.x +-} +mapToKeys :: Map k v -> Slist k +mapToKeys m = Slist + { sList = Map.keys m + , sSize = Size $ Map.size m + } +{-# INLINE mapToKeys #-} + +{- | @O(n)@. +Returns a 'Slist' of all key-value pairs of the map in the ascending order of their keys. + +@since x.x.x.x +-} +mapToPairs :: Map k v -> Slist (k, v) +mapToPairs m = Slist + { sList = Map.toAscList m + , sSize = Size $ Map.size m + } +{-# INLINE mapToPairs #-} + +{- | @O(n)@. +Returns a 'Slist' of all elements of the set in the ascending order. + +@since x.x.x.x +-} +setToSlist :: Set a -> Slist a +setToSlist s = Slist + { sList = Set.elems s + , sSize = Size $ Set.size s + } +{-# INLINE setToSlist #-} diff --git a/src/Slist/Maybe.hs b/src/Slist/Maybe.hs index 9d0d0b4..336a4d8 100644 --- a/src/Slist/Maybe.hs +++ b/src/Slist/Maybe.hs @@ -3,12 +3,14 @@ {- | Copyright: (c) 2021 Kowainik SPDX-License-Identifier: MPL-2.0 -Maintainer: Kowainik +Maintainer: Kowainik Stability: Stable Portability: Portable Useful 'Maybe' combinators to work with the 'Maybe' data type and 'Slist' together. + +@since x.x.x.x -} module Slist.Maybe ( maybeToSlist @@ -33,6 +35,8 @@ import qualified Data.Maybe as M Slist {sList = [42], sSize = Size 1} >>> maybeToSlist Nothing Slist {sList = [], sSize = Size 0} + +@since x.x.x.x -} maybeToSlist :: Maybe a -> Slist a maybeToSlist = maybe mempty one @@ -65,6 +69,8 @@ Reverse is right only on singleton/empty lists @ maybeToList . slistToMaybe {empty, singleton slist} ≡ {empty, singleton slist} @ + +@since x.x.x.x -} slistToMaybe :: Slist a -> Maybe a slistToMaybe = foldr (const . Just) Nothing @@ -77,6 +83,8 @@ slistToMaybe = foldr (const . Just) Nothing >>> catMaybes (cons (Just 1) $ cons Nothing $ one $ Just 3) Slist {sList = [1,3], sSize = Size 2} + +@since x.x.x.x -} catMaybes :: Slist (Maybe a) -> Slist a catMaybes = mapMaybe id @@ -97,6 +105,8 @@ If we map the 'Just' constructor, the entire list should be returned: >>> mapMaybe Just s Slist {sList = [1,2,3], sSize = Size 3} + +@since x.x.x.x -} mapMaybe :: forall b a . (a -> Maybe b) -> Slist a -> Slist b mapMaybe _ (Slist [] _) = mempty @@ -115,6 +125,8 @@ mapMaybe f (Slist (x:xs) n) = case f x of >>> slistWith maybeEven [1,2,3] Slist {sList = [2], sSize = Size 1} + +@since x.x.x.x -} slistWith :: forall b a . (a -> Maybe b) -> [a] -> Slist b slistWith f l = let (n, sl) = go 0 l in Slist sl (Size n)