packages feed

grouped-list 0.1.2.0 → 0.1.3.0

raw patch · 5 files changed

+182/−8 lines, 5 filesdep +QuickCheckdep +tastydep +tasty-quickcheckdep ~basePVP ok

version bump matches the API change (PVP)

Dependencies added: QuickCheck, tasty, tasty-quickcheck

Dependency ranges changed: base

API changes (from Hackage documentation)

+ Data.GroupedList: drop :: Int -> Grouped a -> Grouped a
+ Data.GroupedList: instance GHC.Classes.Eq a => GHC.Exts.IsList (Data.GroupedList.Grouped a)
+ Data.GroupedList: take :: Int -> Grouped a -> Grouped a

Files

CHANGELOG.md view
@@ -1,4 +1,8 @@ +# 0.1.2.0++* Addition of ``adjustM``.+ # 0.1.1.0 -* Addition of 'empty'.+* Addition of ``empty``.
Data/GroupedList.hs view
@@ -1,11 +1,12 @@ -{-# LANGUAGE TupleSections #-}+{-# LANGUAGE TupleSections, TypeFamilies #-}  -- | Grouped lists are like lists, but internally they are represented --   as groups of consecutive elements. -- --   For example, the list @[1,2,2,3,4,5,5,5]@ would be internally---   represented as @[[1],[2,2],[3],[4],[5,5,5]]@.+--   represented as @[[1],[2,2],[3],[4],[5,5,5]]@. Use 'groupedGroups'+--   to see this. -- module Data.GroupedList   ( -- * Type@@ -13,6 +14,7 @@     -- * Builders   , empty   , point+    -- | Use 'point' to create a 'Grouped' list with a single element.   , concatMap   , replicate   , fromGroup@@ -20,6 +22,9 @@   , index   , adjust   , adjustM+    -- * Sublists+  , take+  , drop     -- * Mapping   , map     -- * Traversal@@ -32,6 +37,7 @@     -- * Sorting   , sort     -- * List conversion+    -- | For to-list conversion use 'toList'.   , fromList     -- * Groups   , Group@@ -41,7 +47,9 @@     ) where  import Prelude hiding-  (concat, concatMap, replicate, filter, map)+  ( concat, concatMap, replicate, filter, map+  , take, drop+    ) import qualified Prelude as Prelude import Data.Pointed import Data.Foldable (toList, fold, foldrM)@@ -53,6 +61,7 @@ import Control.Arrow (second) import qualified Data.Map.Strict as M import Data.Functor.Identity (Identity (..))+import qualified GHC.Exts as GHC  ------------------------------------------------------------------ ------------------------------------------------------------------@@ -124,10 +133,18 @@ empty :: Grouped a empty = Grouped S.empty +-- | Method 'fromList' doesn't work for infinite lists.+--   A grouped list cannot be infinite.+instance Eq a => GHC.IsList (Grouped a) where+  type (Item (Grouped a)) = a+  fromList = Grouped . S.fromList . fmap (\g -> Group (length g) $ head g) . group+  toList = toList+ -- | Build a grouped list from a regular list. It doesn't work if---   the input list is infinite.+--   the input list is infinite. This is just a specialized version+--   of 'GHC.fromList'. fromList :: Eq a => [a] -> Grouped a-fromList = Grouped . S.fromList . fmap (\g -> Group (length g) $ head g) . group+fromList = GHC.fromList  -- | Build a grouped list from a group (see 'Group'). fromGroup :: Group a -> Grouped a@@ -226,7 +243,7 @@          then (fromGroup g <> gtrue,gfalse)          else (gtrue,fromGroup g <> gfalse) --- | Filter a grouped list by keeping only those that match a given condition.+-- | Filter a grouped list by keeping only those elements that match a given condition. filter :: Eq a => (a -> Bool) -> Grouped a -> Grouped a filter f = fst . partition f @@ -251,6 +268,7 @@ adjust :: Eq a => (a -> a) -> Int -> Grouped a -> Grouped a adjust f i g = runIdentity $ adjustM (Identity . f) i g +-- | Just like 'adjust', but the function returns in a 'Monad'. adjustM :: (Monad m, Eq a) => (a -> m a) -> Int -> Grouped a -> m (Grouped a) adjustM f k g@(Grouped gs) = if k < 0 then pure g else Grouped <$> go 0 k gs   where@@ -331,6 +349,36 @@                 -- Note: n < i  ==>  0 < i - n             _ | otherwise -> go (npre+1) (i-n) xs         _ -> pure S.empty++------------------------------------------------------------------+------------------------------------------------------------------+-- Sublists++-- | Take the given number of elements from the left end of the+--   list.+take :: Int -> Grouped a -> Grouped a+take n (Grouped gs) = Grouped $ if n <= 0 then S.empty else go 0 n gs+  where+    go npre k xs =+      case S.viewl xs of+        Group q x S.:< ys ->+          if k <= q+             then S.take npre gs S.|> Group k x+             else go (npre+1) (k-q) ys -- k - q > 0+        _ -> gs++-- | Discard the given number of elements from the left end of+--   the list.+drop :: Int -> Grouped a -> Grouped a+drop n g@(Grouped gs) = if n <= 0 then g else Grouped $ go n gs+  where+    go k xs =+      case S.viewl xs of+        Group q x S.:< ys ->+          if k < q+             then Group (q - k) x S.<| ys+             else go (k - q) ys+        _ -> S.empty  ------------------------------------------------------------------ ------------------------------------------------------------------
grouped-list.cabal view
@@ -1,5 +1,5 @@ name:                grouped-list-version:             0.1.2.0+version:             0.1.3.0 synopsis:            Grouped lists. Equal consecutive elements are grouped. description:   Grouped lists work like regular lists, except for two conditions:@@ -47,3 +47,20 @@ source-repository head   type: git   location: https://github.com/Daniel-Diaz/grouped-list.git++test-suite grouped-list-examples+  default-language: Haskell2010+  type: exitcode-stdio-1.0+  hs-source-dirs: test+  main-is: examples.hs+  build-depends: base >= 4.8, grouped-list++test-suite grouped-list-properties+  default-language: Haskell2010+  type: exitcode-stdio-1.0+  hs-source-dirs: test+  main-is: properties.hs+  ghc-options: -Wall+  build-depends:+      base >= 4.8, grouped-list+    , QuickCheck, tasty, tasty-quickcheck
+ test/examples.hs view
@@ -0,0 +1,59 @@++{-# LANGUAGE OverloadedLists #-}++module Main (main) where++import System.Exit (exitFailure)+import Data.GroupedList (Grouped)+import qualified Data.GroupedList as GL+import Control.Monad (unless)++(=:) :: (Show a, Eq a) => a -> a -> IO ()+x =: y = unless (x == y) $ do+  print x+  putStrLn " /= "+  print y+  exitFailure++main :: IO ()+main = do+  (GL.empty :: Grouped ())+    =: []+  (GL.point 0 :: Grouped Int)+    =: [0]+  GL.replicate 3 0+    =: [0,0,0]+  GL.index [1,2,3] 1+    =: Just 2+  GL.index [1,2,3] 3+    =: Nothing+  GL.adjust (+1) 1 [1,2,2]+    =: [1,3,2]+  GL.adjust (+1) 3 [1,2,3]+    =: [1,2,3]+  GL.take 2 [1,1,1,2]+    =: [1,1]+  GL.take 2 [1,2,2,2]+    =: [1,2]+  GL.take 2 [1,2,3,4]+    =: [1,2]+  GL.drop 2 [1,1,1,2]+    =: [1,2]+  GL.drop 2 [1,2,2,2]+    =: [2,2]+  GL.drop 2 [1,2,3,4]+    =: [3,4] +  GL.map (+1) [1,2,3,4]+    =: [2,3,4,5]+  GL.map (+1) [1,1,2,3]+    =: [2,2,3,4]+  GL.map (const 0) [1,2,2,3]+    =: [0,0,0,0]+  GL.partition even [1,2,3,4]+    =: ([2,4],[1,3])+  GL.partition even [1,2,1,2]+    =: ([2,2],[1,1])+  GL.sort [1,2,1,2]+    =: [1,1,2,2]+  GL.sort [3,2,1,3]+    =: [1,2,3,3]
+ test/properties.hs view
@@ -0,0 +1,46 @@++module Main (main) where++import Data.GroupedList (Grouped)+import qualified Data.GroupedList as GL+import Data.Monoid ((<>))+import Data.Foldable (toList)++import Test.Tasty+import qualified Test.Tasty.QuickCheck as QC+import Test.QuickCheck (Arbitrary (..), Testable, choose)++-- | A short alias for 'QC.testProperty'.+prop :: Testable a => TestName -> a -> TestTree+prop = QC.testProperty++newtype G = G (Grouped Int)++instance Show G where+  show (G g) = show g++instance Arbitrary G where+  arbitrary = G . GL.fromList <$> arbitrary++main :: IO ()+main = defaultMain $ testGroup "Properties"+  [ prop "left mempty" $ \(G xs) -> GL.empty <> xs == xs+  , prop "right mempty" $ \(G xs) -> xs <> GL.empty == xs+  , prop "mappend" $ \(G xs) (G ys) (G zs) -> (xs <> ys) <> zs == xs <> (ys <> zs)+  , prop "index" $ \(G xs) ->+      let n = length xs+      in  (\i -> +             let r = GL.index xs i+             in  if i <= n - 1+                    then r == Just (toList xs !! i)+                    else r == Nothing+            ) <$> choose (0,2*n)+  , prop "split and concat" $ \(G xs) ->+      let n = length xs+      in  (\i -> GL.take i xs <> GL.drop i xs == xs) <$> choose (0,n)+  , prop "map id" $ \(G xs) -> GL.map id xs == xs+  , prop "map (+1) . map (+1) = map (+2)" $+      \(G xs) -> GL.map (+1) (GL.map (+1) xs) == GL.map (+2) xs+  , prop "'map (const i)' generates at most one group" $+      \(G xs) i -> length (GL.groupedGroups $ GL.map (const i) xs :: [GL.Group Int]) <= 1+    ]