{-# LANGUAGE BangPatterns #-} {-# LANGUAGE ScopedTypeVariables #-} module Data.Concatenation ( concatSized , concatSized1 ) where import Data.List.NonEmpty (NonEmpty((:|))) import qualified Data.List as L import qualified Data.List.NonEmpty as NE -- | Concatenate all the values in the list in the order they -- are given. This function attempts to perform smaller concatenations -- together. This is good for data structures that do not take -- advantage of sharing. concatSized :: forall m. (m -> Int) -- size function -> m -> (m -> m -> m) -> [m] -> m concatSized size empty combine = go [] where go :: [m] -> [m] -> m go !stack [] = L.foldl' combine empty (L.reverse stack) go !stack (x : xs) = go (pushStack x stack) xs pushStack :: m -> [m] -> [m] pushStack x [] = [x] pushStack x (s : ss) = if size x >= size s then pushStack (combine s x) ss else x : s : ss -- | This function is likely to be used for things like intersection -- where the zero-sized element is not an identity but a zero. concatSized1 :: forall m. (m -> Int) -- size function -> (m -> m -> m) -> NonEmpty m -> m concatSized1 size combine (p :| ps) = go (p :| []) ps where go :: NonEmpty m -> [m] -> m go !stack [] = safeFoldl1' combine (NE.reverse stack) go !stack (x : xs) = go (pushStack x stack) xs pushStack :: m -> NonEmpty m -> NonEmpty m pushStack x (s :| ss) = if size x >= size s then case ss of [] -> combine s x :| [] r : rs -> pushStack (combine s x) (r :| rs) else x :| (s : ss) safeFoldl1' :: (a -> a -> a) -> NonEmpty a -> a safeFoldl1' f (a :| as) = L.foldl' f a as