-------------------------------------------------------------------------- -- | -- Module : Sidekick -- Copyright : (c) Thanos Tsouanas 2010 -- License : BSD -- Maintainer : thanos@sians.org -- Stability : experimental -- -- General-purpose helper functions. -- -------------------------------------------------------------------------- module Sidekick ( -- * List processing takeWhileUnique , joinWith , joinWith' , functionize , headfeet , protectWith -- * Pretty-printing , showAll -- * Boolean testing , passAny , passAll , failAny , failAll ) where -------------------------------------------------------------------------- import Maybe ( fromMaybe ) -------------------------------------------------------------------------- -- | Keep taking until you reach two consequtive same values. takeWhileUnique :: Eq a => [a] -> [a] takeWhileUnique [] = [] takeWhileUnique [x] = [x] takeWhileUnique (x1:x2:xs) | x1 == x2 = [x1] | otherwise = x1 : takeWhileUnique (x2:xs) -- | Try to find a fixpoint of the given function by -- iterations starting from the given value. fix :: Eq a => (a -> a) -> a -> a fix f = last . takeWhileUnique . (iterate f) -- | Join a list of lists together, using another list as a separator. -- Like python's @sep.join(list)@. joinWith :: [a] -> [[a]] -> [a] joinWith sep [] = [] joinWith sep [x] = x joinWith sep (x:xs) = x ++ sep ++ (joinWith sep xs) -- | Like 'joinWith', but it accepts start and end terms. joinWith' :: [a] -> [a] -> [a] -> [[a]] -> [a] joinWith' a z sep xs = a ++ joinWith sep xs ++ z -- | Display each showable item of a list on a separate line. showAll :: Show a => (a -> String) -> [a] -> String showAll shower = unlines . map shower -- | This is a smarter version of 'takeWhileUnique', in the sense -- that it looks all the way back to find a repetition, while -- 'takeWhileUnique' only looks at the previous value. -- -- e.g. -- @takeWhileUnique [0, 1, 2, 0, 1, 2, ...]@ is bottom, -- while 'protectWith' would return @[0, 1, 2, guard]@. protectWith :: Eq a => a -> [a] -> [a] -> [a] protectWith = protectWith' [] where protectWith' :: Eq a => [a] -> a -> [a] -> [a] -> [a] protectWith' trace guard terminal [] = [] protectWith' trace guard terminal (x:xs) | x `elem` terminal = [] | x `elem` trace = [x, guard] | otherwise = x : protectWith' (x:trace) guard terminal xs -- | From a given list of tests, check if a given element satisfies any of them. passAny :: [a -> Bool] -> a -> Bool passAny = (or .) . testResults -- | From a given list of tests, check if a given element satisfies all of them. passAll :: [a -> Bool] -> a -> Bool passAll = (and .) . testResults -- | From a given list of tests, check if a given element fails all of them. failAll :: [a -> Bool] -> a -> Bool failAll = (not .) . passAny -- | From a given list of tests, check if a given element fails any of them. failAny :: [a -> Bool] -> a -> Bool failAny = (not .) . passAll -- | Perform a series of given tests on a given element and return the results. testResults :: [a -> Bool] -> a -> [Bool] testResults tests t = [ test t | test <- tests ] -- | Get rid of the body of a list. headfeet :: [a] -> [a] headfeet [] = [] headfeet [x] = [x] headfeet [x,y] = [x,y] headfeet xs = [head xs, last xs] -- | Turn a list of pairs into a function, where where later values overwrite -- earlier ones. functionize [] = [] functionize (p@(k,_):ps) = case lookup k ps of Just v' -> functionize ps _______ -> p : functionize ps