{-| Description : List utilities -} module Language.Haskell.Formatter.Toolkit.ListTool (maybeLast, dropWhileAtMost, mergeLongerSuccessions, takeEvery, concatenateRuns, concatenateShiftedRuns) where import qualified Data.List as List import qualified Data.Maybe as Maybe import qualified Data.Monoid as Monoid {-| The last element, or 'Nothing' if there is none. prop> maybeLast [] == Nothing prop> maybeLast (l ++ [e]) == Just e -} maybeLast :: [a] -> Maybe a maybeLast = Maybe.listToMaybe . reverse {-| @dropWhileAtMost p l@ is like @dropWhile p@, but drops at most @l@ elements. >>> dropWhileAtMost (== ' ') 2 " a bc " " a bc " -} dropWhileAtMost :: (a -> Bool) -> Int -> [a] -> [a] dropWhileAtMost predicate limit list = Monoid.mappend (dropWhile predicate deformable) rigid where (deformable, rigid) = splitAt limit list {-| @mergeLongerSuccessions p c l@ keeps only the first @c@ elements of successive elements of @l@ satisfying the predicate @p@. >>> mergeLongerSuccessions Data.Char.isSpace 2 " ab c d\LF e " " ab c d\n e " -} mergeLongerSuccessions :: (a -> Bool) -> Int -> [a] -> [a] mergeLongerSuccessions predicate count = snd . List.foldl' merge (0, []) where merge (successionLength, list) element = if predicate element then if successionLength < count then (succ successionLength, extended) else (count, list) else (0, extended) where extended = Monoid.mappend list [element] {-| @takeEvery p l@ takes every @p@th element of @l@ from the first one. >>> takeEvery 2 "apple" "ape" prop> takeEvery 1 l == l -} takeEvery :: Int -> [a] -> [a] takeEvery _ [] = [] takeEvery period list@(first : _) = first : takeEvery period (drop period list) {-| @concatenateRuns p l@ repeatedly concatenates @p@ lists of @l@. >>> concatenateRuns 2 ["a", "b", "c", "d", "e"] ["ab","cd","e"] -} concatenateRuns :: Int -> [[a]] -> [[a]] concatenateRuns _ [] = [] concatenateRuns period lists = concat run : concatenateRuns period rest where (run, rest) = splitAt period lists {-| @concatenateShiftedRuns p s l@ first takes @s@ lists of @l@, followed by repeatedly concatenating @p@ lists. >>> concatenateShiftedRuns 2 1 ["a", "b", "c", "d", "e"] ["a","bc","de"] prop> p <= 0 || concatenateShiftedRuns p 0 l == concatenateRuns p l -} concatenateShiftedRuns :: Int -> Int -> [[a]] -> [[a]] concatenateShiftedRuns period shift lists = case shift of 0 -> concatenateUnshifted lists _ -> concat shifted : concatenateUnshifted unshifted where (shifted, unshifted) = splitAt shift lists where concatenateUnshifted = concatenateRuns period