extra 1.0.1 → 1.1
raw patch · 8 files changed
+118/−7 lines, 8 filesPVP ok
version bump matches the API change (PVP)
API changes (from Hackage documentation)
- System.Time.Extra: instance Eq Timeout
- System.Time.Extra: instance Exception Timeout
- System.Time.Extra: instance Show Timeout
- System.Time.Extra: instance Typeable Timeout
+ Data.List.Extra: groupSortBy :: (a -> a -> Ordering) -> [a] -> [[a]]
+ Data.List.Extra: groupSortOn :: Ord b => (a -> b) -> [a] -> [[a]]
+ Data.List.Extra: instance GHC.Show.Show Data.List.Extra.Color
+ Data.List.Extra: instance GHC.Show.Show a => GHC.Show.Show (Data.List.Extra.RB a)
+ Data.List.Extra: nubOrd :: Ord a => [a] -> [a]
+ Data.List.Extra: nubOrdBy :: (a -> a -> Ordering) -> [a] -> [a]
+ Data.List.Extra: nubOrdOn :: Ord b => (a -> b) -> [a] -> [a]
+ Data.List.Extra: splitAtEnd :: Int -> [a] -> ([a], [a])
+ Extra: groupSortBy :: (a -> a -> Ordering) -> [a] -> [[a]]
+ Extra: groupSortOn :: Ord b => (a -> b) -> [a] -> [[a]]
+ Extra: nubOrd :: Ord a => [a] -> [a]
+ Extra: nubOrdBy :: (a -> a -> Ordering) -> [a] -> [a]
+ Extra: nubOrdOn :: Ord b => (a -> b) -> [a] -> [a]
+ Extra: splitAtEnd :: Int -> [a] -> ([a], [a])
+ System.Time.Extra: instance GHC.Classes.Eq System.Time.Extra.Timeout
+ System.Time.Extra: instance GHC.Exception.Exception System.Time.Extra.Timeout
+ System.Time.Extra: instance GHC.Show.Show System.Time.Extra.Timeout
Files
- CHANGES.txt +4/−0
- LICENSE +1/−1
- extra.cabal +2/−2
- src/Control/Monad/Extra.hs +1/−1
- src/Data/List/Extra.hs +97/−2
- src/Extra.hs +1/−1
- test/TestGen.hs +11/−0
- test/TestUtil.hs +1/−0
CHANGES.txt view
@@ -1,5 +1,9 @@ Changelog for Extra +1.1+ #7, add nubOrd, nubOrdOn, nubOrdBy+ #6, add groupSortOn and groupSortBy+ #5, add splitAtEnd 1.0.1 Make listFilesAvoid drop trailing path separators before testing #3, add a constraint base >= 4.4
LICENSE view
@@ -1,4 +1,4 @@-Copyright Neil Mitchell 2014.+Copyright Neil Mitchell 2014-2015. All rights reserved. Redistribution and use in source and binary forms, with or without
extra.cabal view
@@ -1,13 +1,13 @@ cabal-version: >= 1.10 build-type: Simple name: extra-version: 1.0.1+version: 1.1 license: BSD3 license-file: LICENSE category: Development author: Neil Mitchell <ndmitchell@gmail.com> maintainer: Neil Mitchell <ndmitchell@gmail.com>-copyright: Neil Mitchell 2014+copyright: Neil Mitchell 2014-2015 synopsis: Extra functions I use. description: A library of extra functions for the standard Haskell libraries. Most functions are simple additions, filling out missing functionality. A few functions are available in later versions of GHC, but this package makes them available back to GHC 7.2.
src/Control/Monad/Extra.hs view
@@ -32,7 +32,7 @@ whenJust mg f = maybe (pure ()) f mg -- | The identity function which requires the inner argument to be @()@. Useful for functions--- with overloaded return times.+-- with overloaded return types. -- -- > \(x :: Maybe ()) -> unit x == x unit :: m () -> m ()
src/Data/List/Extra.hs view
@@ -10,14 +10,16 @@ -- * String operations lower, upper, trim, trimStart, trimEnd, word1, -- * Splitting - dropEnd, takeEnd, breakEnd, spanEnd,+ dropEnd, takeEnd, splitAtEnd, breakEnd, spanEnd, dropWhileEnd, dropWhileEnd', takeWhileEnd, stripSuffix, wordsBy, linesBy, breakOn, breakOnEnd, splitOn, split, chunksOf, -- * Basics list, uncons, unsnoc, cons, snoc, drop1, -- * List operations- groupSort, nubOn, groupOn, sortOn,+ groupSort, groupSortOn, groupSortBy,+ nubOrd, nubOrdBy, nubOrdOn,+ nubOn, groupOn, sortOn, disjoint, allSame, anySame, repeatedly, for, firstJust, concatUnzip, concatUnzip3,@@ -153,6 +155,19 @@ f _ _ = [] +-- | @'splitAtEnd' n xs@ returns a split where the second element tries to+-- contain @n@ elements.+--+-- > splitAtEnd 3 "hello" == ("he","llo")+-- > splitAtEnd 3 "he" == ("", "he")+-- > \i xs -> uncurry (++) (splitAt i xs) == xs+-- > \i xs -> splitAtEnd i xs == (dropEnd i xs, takeEnd i xs)+splitAtEnd :: Int -> [a] -> ([a], [a])+splitAtEnd i xs = f xs (drop i xs)+ where f (x:xs) (y:ys) = first (x:) $ f xs ys+ f xs _ = ([], xs)++ -- | A merging of 'unzip' and 'concat'. -- -- > concatUnzip [("a","AB"),("bc","C")] == ("abc","ABC")@@ -244,6 +259,7 @@ nubOn :: Eq b => (a -> b) -> [a] -> [a] nubOn f = map snd . nubBy ((==) `on` fst) . map (\x -> let y = f x in y `seq` (y, x)) + -- | A combination of 'group' and 'sort'. -- -- > groupSort [(1,'t'),(3,'t'),(2,'e'),(2,'s')] == [(1,"t"),(2,"es"),(3,"t")]@@ -252,7 +268,19 @@ groupSort :: Ord k => [(k, v)] -> [(k, [v])] groupSort = map (\x -> (fst $ head x, map snd x)) . groupOn fst . sortOn fst +-- | A combination of 'group' and 'sort', using a part of the value to compare on.+--+-- > groupSortOn length ["test","of","sized","item"] == [["of"],["test","item"],["sized"]]+groupSortOn :: Ord b => (a -> b) -> [a] -> [[a]]+groupSortOn f = map (map snd) . groupBy ((==) `on` fst) . sortBy (compare `on` fst) . map (f &&& id) +-- | A combination of 'group' and 'sort', using a predicate to compare on.+--+-- > groupSortBy (compare `on` length) ["test","of","sized","item"] == [["of"],["test","item"],["sized"]]+groupSortBy :: (a -> a -> Ordering) -> [a] -> [[a]]+groupSortBy f = groupBy (\a b -> f a b == EQ) . sortBy f++ -- | Merge two lists which are assumed to be ordered. -- -- > merge "ace" "bd" == "abcde"@@ -449,3 +477,70 @@ chunksOf :: Int -> [a] -> [[a]] chunksOf i xs | i <= 0 = error $ "chunksOf, number must be positive, got " ++ show i chunksOf i xs = repeatedly (splitAt i) xs+++-- | /O(n log n)/. The 'nubOrd' function removes duplicate elements from a list.+-- In particular, it keeps only the first occurrence of each element.+-- Unlike the standard 'nub' operator, this version requires an 'Ord' instance+-- and consequently runs asymptotically faster.+--+-- > nubOrd "this is a test" == "this ae"+-- > nubOrd (take 4 ("this" ++ undefined)) == "this"+-- > \xs -> nubOrd xs == nub xs+nubOrd :: Ord a => [a] -> [a]+nubOrd = nubOrdBy compare++-- | A version of 'nubOrd' which operates on a portion of the value.+--+-- > nubOrdOn length ["a","test","of","this"] == ["a","test","of"]+nubOrdOn :: Ord b => (a -> b) -> [a] -> [a]+nubOrdOn f = map snd . nubOrdBy (compare `on` fst) . map (f &&& id)++-- | A version of 'nubOrd' with a custom predicate.+--+-- > nubOrdBy (compare `on` length) ["a","test","of","this"] == ["a","test","of"]+nubOrdBy :: (a -> a -> Ordering) -> [a] -> [a]+nubOrdBy cmp xs = f E xs+ where f seen [] = []+ f seen (x:xs) | memberRB cmp x seen = f seen xs+ | otherwise = x : f (insertRB cmp x seen) xs++---------------------------------------------------------------------+-- OKASAKI RED BLACK TREE+-- Taken from http://www.cs.kent.ac.uk/people/staff/smk/redblack/Untyped.hs++data Color = R | B deriving Show+data RB a = E | T Color (RB a) a (RB a) deriving Show++{- Insertion and membership test as by Okasaki -}+insertRB :: (a -> a -> Ordering) -> a -> RB a -> RB a+insertRB cmp x s =+ T B a z b+ where+ T _ a z b = ins s+ ins E = T R E x E+ ins s@(T B a y b) = case cmp x y of+ LT -> balance (ins a) y b+ GT -> balance a y (ins b)+ EQ -> s+ ins s@(T R a y b) = case cmp x y of+ LT -> T R (ins a) y b+ GT -> T R a y (ins b)+ EQ -> s++memberRB :: (a -> a -> Ordering) -> a -> RB a -> Bool+memberRB cmp x E = False+memberRB cmp x (T _ a y b) = case cmp x y of+ LT -> memberRB cmp x a+ GT -> memberRB cmp x b+ EQ -> True++{- balance: first equation is new,+ to make it work with a weaker invariant -}+balance :: RB a -> a -> RB a -> RB a+balance (T R a x b) y (T R c z d) = T R (T B a x b) y (T B c z d)+balance (T R (T R a x b) y c) z d = T R (T B a x b) y (T B c z d)+balance (T R a x (T R b y c)) z d = T R (T B a x b) y (T B c z d)+balance a x (T R b y (T R c z d)) = T R (T B a x b) y (T B c z d)+balance a x (T R (T R b y c) z d) = T R (T B a x b) y (T B c z d)+balance a x b = T B a x b
src/Extra.hs view
@@ -20,7 +20,7 @@ modifyIORef', writeIORef', atomicModifyIORef', atomicWriteIORef, atomicWriteIORef', -- * Data.List.Extra -- | Extra functions available in @"Data.List.Extra"@.- lower, upper, trim, trimStart, trimEnd, word1, dropEnd, takeEnd, breakEnd, spanEnd, dropWhileEnd, dropWhileEnd', takeWhileEnd, stripSuffix, wordsBy, linesBy, breakOn, breakOnEnd, splitOn, split, chunksOf, list, uncons, unsnoc, cons, snoc, drop1, groupSort, nubOn, groupOn, sortOn, disjoint, allSame, anySame, repeatedly, for, firstJust, concatUnzip, concatUnzip3, replace, merge, mergeBy,+ lower, upper, trim, trimStart, trimEnd, word1, dropEnd, takeEnd, splitAtEnd, breakEnd, spanEnd, dropWhileEnd, dropWhileEnd', takeWhileEnd, stripSuffix, wordsBy, linesBy, breakOn, breakOnEnd, splitOn, split, chunksOf, list, uncons, unsnoc, cons, snoc, drop1, groupSort, groupSortOn, groupSortBy, nubOrd, nubOrdBy, nubOrdOn, nubOn, groupOn, sortOn, disjoint, allSame, anySame, repeatedly, for, firstJust, concatUnzip, concatUnzip3, replace, merge, mergeBy, -- * Data.Tuple.Extra -- | Extra functions available in @"Data.Tuple.Extra"@. first, second, (***), (&&&), dupe, both, fst3, snd3, thd3,
test/TestGen.hs view
@@ -84,6 +84,10 @@ testGen "\\i xs -> dropEnd i xs `isPrefixOf` xs" $ \i xs -> dropEnd i xs `isPrefixOf` xs testGen "\\i xs -> length (dropEnd i xs) == max 0 (length xs - max 0 i)" $ \i xs -> length (dropEnd i xs) == max 0 (length xs - max 0 i) testGen "\\i -> take 3 (dropEnd 5 [i..]) == take 3 [i..]" $ \i -> take 3 (dropEnd 5 [i..]) == take 3 [i..]+ testGen "splitAtEnd 3 \"hello\" == (\"he\",\"llo\")" $ splitAtEnd 3 "hello" == ("he","llo")+ testGen "splitAtEnd 3 \"he\" == (\"\", \"he\")" $ splitAtEnd 3 "he" == ("", "he")+ testGen "\\i xs -> uncurry (++) (splitAt i xs) == xs" $ \i xs -> uncurry (++) (splitAt i xs) == xs+ testGen "\\i xs -> splitAtEnd i xs == (dropEnd i xs, takeEnd i xs)" $ \i xs -> splitAtEnd i xs == (dropEnd i xs, takeEnd i xs) testGen "concatUnzip [(\"a\",\"AB\"),(\"bc\",\"C\")] == (\"abc\",\"ABC\")" $ concatUnzip [("a","AB"),("bc","C")] == ("abc","ABC") testGen "concatUnzip3 [(\"a\",\"AB\",\"\"),(\"bc\",\"C\",\"123\")] == (\"abc\",\"ABC\",\"123\")" $ concatUnzip3 [("a","AB",""),("bc","C","123")] == ("abc","ABC","123") testGen "takeWhileEnd even [2,3,4,6] == [4,6]" $ takeWhileEnd even [2,3,4,6] == [4,6]@@ -104,6 +108,8 @@ testGen "groupSort [(1,'t'),(3,'t'),(2,'e'),(2,'s')] == [(1,\"t\"),(2,\"es\"),(3,\"t\")]" $ groupSort [(1,'t'),(3,'t'),(2,'e'),(2,'s')] == [(1,"t"),(2,"es"),(3,"t")] testGen "\\xs -> map fst (groupSort xs) == sort (nub (map fst xs))" $ \xs -> map fst (groupSort xs) == sort (nub (map fst xs)) testGen "\\xs -> concatMap snd (groupSort xs) == map snd (sortOn fst xs)" $ \xs -> concatMap snd (groupSort xs) == map snd (sortOn fst xs)+ testGen "groupSortOn length [\"test\",\"of\",\"sized\",\"item\"] == [[\"of\"],[\"test\",\"item\"],[\"sized\"]]" $ groupSortOn length ["test","of","sized","item"] == [["of"],["test","item"],["sized"]]+ testGen "groupSortBy (compare `on` length) [\"test\",\"of\",\"sized\",\"item\"] == [[\"of\"],[\"test\",\"item\"],[\"sized\"]]" $ groupSortBy (compare `on` length) ["test","of","sized","item"] == [["of"],["test","item"],["sized"]] testGen "merge \"ace\" \"bd\" == \"abcde\"" $ merge "ace" "bd" == "abcde" testGen "\\xs ys -> merge (sort xs) (sort ys) == sort (xs ++ ys)" $ \xs ys -> merge (sort xs) (sort ys) == sort (xs ++ ys) testGen "replace \"el\" \"_\" \"Hello Bella\" == \"H_lo B_la\"" $ replace "el" "_" "Hello Bella" == "H_lo B_la"@@ -153,6 +159,11 @@ testGen "chunksOf 3 \"mytest\" == [\"myt\",\"est\"]" $ chunksOf 3 "mytest" == ["myt","est"] testGen "chunksOf 8 \"\" == []" $ chunksOf 8 "" == [] testGen "chunksOf 0 \"test\" == undefined" $ erroneous $ chunksOf 0 "test"+ testGen "nubOrd \"this is a test\" == \"this ae\"" $ nubOrd "this is a test" == "this ae"+ testGen "nubOrd (take 4 (\"this\" ++ undefined)) == \"this\"" $ nubOrd (take 4 ("this" ++ undefined)) == "this"+ testGen "\\xs -> nubOrd xs == nub xs" $ \xs -> nubOrd xs == nub xs+ testGen "nubOrdOn length [\"a\",\"test\",\"of\",\"this\"] == [\"a\",\"test\",\"of\"]" $ nubOrdOn length ["a","test","of","this"] == ["a","test","of"]+ testGen "nubOrdBy (compare `on` length) [\"a\",\"test\",\"of\",\"this\"] == [\"a\",\"test\",\"of\"]" $ nubOrdBy (compare `on` length) ["a","test","of","this"] == ["a","test","of"] testGen "first succ (1,\"test\") == (2,\"test\")" $ first succ (1,"test") == (2,"test") testGen "second reverse (1,\"test\") == (1,\"tset\")" $ second reverse (1,"test") == (1,"tset") testGen "(succ *** reverse) (1,\"test\") == (2,\"tset\")" $ (succ *** reverse) (1,"test") == (2,"tset")
test/TestUtil.hs view
@@ -15,6 +15,7 @@ import Extra as X import Control.Applicative as X import Control.Monad as X+import Data.Function as X import Data.List as X import Data.Char as X import Data.Tuple as X