----------------------------------------------------------------------------- -- $Id: GenUtils.lhs,v 1.1 1999/11/12 11:54:17 simonmar Exp $ -- Some General Utilities, including sorts, etc. -- This is realy just an extended prelude. -- All the code below is understood to be in the public domain. ----------------------------------------------------------------------------- > module GenUtils ( > partition', tack, > assocMaybeErr, > arrElem, > memoise, > returnMaybe,handleMaybe, findJust, > MaybeErr(..), > maybeMap, > joinMaybe, > mkClosure, > foldb, > sortWith, > sort, > cjustify, > ljustify, > rjustify, > space, > copy, > combinePairs, > --trace, -- re-export it > fst3, > snd3, > thd3 #if __HASKELL1__ < 3 || ( defined(__GLASGOW_HASKELL__) && __GLASGOW_HASKELL__ < 200 ) > ,Cmp(..), compare, lookup, isJust #endif > ) where #if __HASKELL1__ >= 3 && ( !defined(__GLASGOW_HASKELL__) || __GLASGOW_HASKELL__ >= 200 ) > import Ix ( Ix(..) ) > import Array ( listArray, array, (!) ) #define Text Show #define ASSOC(a,b) (a , b) #else #define ASSOC(a,b) (a := b) #endif %------------------------------------------------------------------------------ Here are two defs that everyone seems to define ... HBC has it in one of its builtin modules #ifdef __GOFER__ primitive primPrint "primPrint" :: Int -> a -> ShowS #endif #ifdef __GOFER__ primitive primGenericEq "primGenericEq", primGenericNe "primGenericNe", primGenericLe "primGenericLe", primGenericLt "primGenericLt", primGenericGe "primGenericGe", primGenericGt "primGenericGt" :: a -> a -> Bool instance Text (Maybe a) where { showsPrec = primPrint } instance Eq (Maybe a) where (==) = primGenericEq (/=) = primGenericNe instance (Ord a) => Ord (Maybe a) where Nothing <= _ = True _ <= Nothing = True (Just a) <= (Just b) = a <= b #endif > maybeMap :: (a -> b) -> Maybe a -> Maybe b > maybeMap f (Just a) = Just (f a) > maybeMap f Nothing = Nothing > joinMaybe :: (a -> a -> a) -> Maybe a -> Maybe a -> Maybe a > joinMaybe _ Nothing Nothing = Nothing > joinMaybe _ (Just g) Nothing = Just g > joinMaybe _ Nothing (Just g) = Just g > joinMaybe f (Just g) (Just h) = Just (f g h) > data MaybeErr a err = Succeeded a | Failed err deriving (Eq,Text) @mkClosure@ makes a closure, when given a comparison and iteration loop. Be careful, because if the functional always makes the object different, This will never terminate. > mkClosure :: (a -> a -> Bool) -> (a -> a) -> a -> a > mkClosure eq f = match . iterate f > where > match (a:b:c) | a `eq` b = a > match (_:c) = match c > foldb :: (a -> a -> a) -> [a] -> a > foldb f [] = error "can't reduce an empty list using foldb" > foldb f [x] = x > foldb f l = foldb f (foldb' l) > where > foldb' (x:y:x':y':xs) = f (f x y) (f x' y') : foldb' xs > foldb' (x:y:xs) = f x y : foldb' xs > foldb' xs = xs Merge two ordered lists into one ordered list. > mergeWith :: (a -> a -> Bool) -> [a] -> [a] -> [a] > mergeWith _ [] ys = ys > mergeWith _ xs [] = xs > mergeWith le (x:xs) (y:ys) > | x `le` y = x : mergeWith le xs (y:ys) > | otherwise = y : mergeWith le (x:xs) ys > insertWith :: (a -> a -> Bool) -> a -> [a] -> [a] > insertWith _ x [] = [x] > insertWith le x (y:ys) > | x `le` y = x:y:ys > | otherwise = y:insertWith le x ys Sorting is something almost every program needs, and this is the quickest sorting function I know of. > sortWith :: (a -> a -> Bool) -> [a] -> [a] > sortWith le [] = [] > sortWith le lst = foldb (mergeWith le) (splitList lst) > where > splitList (a1:a2:a3:a4:a5:xs) = > insertWith le a1 > (insertWith le a2 > (insertWith le a3 > (insertWith le a4 [a5]))) : splitList xs > splitList [] = [] > splitList (r:rs) = [foldr (insertWith le) [r] rs] > sort :: (Ord a) => [a] -> [a] > sort = sortWith (<=) > returnMaybe :: a -> Maybe a > returnMaybe = Just > handleMaybe :: Maybe a -> Maybe a -> Maybe a > handleMaybe m k = case m of > Nothing -> k > _ -> m > findJust :: (a -> Maybe b) -> [a] -> Maybe b > findJust f = foldr handleMaybe Nothing . map f Gofer-like stuff: > fst3 (a,_,_) = a > snd3 (_,a,_) = a > thd3 (_,a,_) = a > cjustify, ljustify, rjustify :: Int -> String -> String > cjustify n s = space halfm ++ s ++ space (m - halfm) > where m = n - length s > halfm = m `div` 2 > ljustify n s = s ++ space (n - length s) > rjustify n s = let s' = take n s in space (n - length s') ++ s' > space :: Int -> String > space n | n < 0 = "" > | otherwise = copy n ' ' > copy :: Int -> a -> [a] -- make list of n copies of x > copy n x = take n xs where xs = x:xs > partition' :: (Eq b) => (a -> b) -> [a] -> [[a]] > partition' f [] = [] > partition' f [x] = [[x]] > partition' f (x:x':xs) | f x == f x' > = tack x (partition' f (x':xs)) > | otherwise > = [x] : partition' f (x':xs) > tack x xss = (x : head xss) : tail xss > combinePairs :: (Ord a) => [(a,b)] -> [(a,[b])] > combinePairs xs = > combine [ (a,[b]) | (a,b) <- sortWith (\ (a,_) (b,_) -> a <= b) xs] > where > combine [] = [] > combine ((a,b):(c,d):r) | a == c = combine ((a,b++d) : r) > combine (a:r) = a : combine r > #if __HASKELL1__ < 3 || ( defined(__GLASGOW_HASKELL__) && __GLASGOW_HASKELL__ < 200 ) > lookup :: (Eq a) => a -> [(a,b)] -> Maybe b > lookup k env = case [ val | (key,val) <- env, k == key] of > [] -> Nothing > (val:vs) -> Just val > > data Cmp = LT | EQ | GT > compare a b | a < b = LT > | a == b = EQ > | otherwise = GT > isJust :: Maybe a -> Bool > isJust (Just _) = True > isJust _ = False #endif > assocMaybeErr :: (Eq a) => [(a,b)] -> a -> MaybeErr b String > assocMaybeErr env k = case [ val | (key,val) <- env, k == key] of > [] -> Failed "assoc: " > (val:vs) -> Succeeded val > Now some utilties involving arrays. Here is a version of @elem@ that uses partual application to optimise lookup. > arrElem :: (Ix a) => [a] -> a -> Bool > arrElem obj = \x -> inRange size x && arr ! x > where > obj' = sort obj > size = (head obj',last obj') > arr = listArray size [ i `elem` obj | i <- range size ] You can use this function to simulate memoisation. For example: > fib = memoise (0,100) fib' > where > fib' 0 = 0 > fib' 1 = 0 > fib' n = fib (n-1) + fib (n-2) will give a very efficent variation of the fib function. > memoise :: (Ix a) => (a,a) -> (a -> b) -> a -> b > memoise bds f = (!) arr > where arr = array bds [ ASSOC(t, f t) | t <- range bds ] > mapAccumR :: (acc -> x -> (acc, y)) -- Function of elt of input list > -- and accumulator, returning new > -- accumulator and elt of result list > -> acc -- Initial accumulator > -> [x] -- Input list > -> (acc, [y]) -- Final accumulator and result list > > mapAccumR f b [] = (b, []) > mapAccumR f b (x:xs) = (b'', x':xs') where > (b'', x') = f b' x > (b', xs') = mapAccumR f b xs > mapAccumL :: (acc -> x -> (acc, y)) -- Function of elt of input list > -- and accumulator, returning new > -- accumulator and elt of result list > -> acc -- Initial accumulator > -> [x] -- Input list > -> (acc, [y]) -- Final accumulator and result list > > mapAccumL f b [] = (b, []) > mapAccumL f b (x:xs) = (b'', x':xs') where > (b', x') = f b x > (b'', xs') = mapAccumL f b' xs Here is the bi-directional version ... > mapAccumB :: (accl -> accr -> x -> (accl, accr,y)) > -- Function of elt of input list > -- and accumulator, returning new > -- accumulator and elt of result list > -> accl -- Initial accumulator from left > -> accr -- Initial accumulator from right > -> [x] -- Input list > -> (accl, accr, [y]) -- Final accumulator and result list > > mapAccumB f a b [] = (a,b,[]) > mapAccumB f a b (x:xs) = (a'',b'',y:ys) > where > (a',b'',y) = f a b' x > (a'',b',ys) = mapAccumB f a' b xs > assert False x = error "assert Failed" > assert True x = x