-- -- String and miscellaneous utilities -- module Utils where import Prelude hiding ( catch ) import MD5 import Data.Char import Data.List import Data.Maybe import System.Locale import System.Time import System.IO import System.Random hiding (split) maybeRead :: (Read a) => String -> Maybe a maybeRead s = case reads s of ((x, []):[]) -> Just x _ -> Nothing splitAtFirst :: Char -> String -> (String, String) splitAtFirst c s = let p = (/=) c in (takeWhile p s, case dropWhile p s of [] -> [] (_:xs) -> xs) formatTime :: String -> IO String formatTime pattern = do clockT <- getClockTime calT <- toCalendarTime clockT return $ formatCalendarTime defaultTimeLocale pattern calT -- Associative lists type AssocList a b = [(a, b)] assocFind :: Eq a => AssocList a b -> a -> Maybe b assocFind l x = case find ((==) x . fst) l of Nothing -> Nothing Just p -> Just (snd p) assocElem :: Eq a => AssocList a b -> a -> Bool assocElem l x = isJust (assocFind l x) md5String :: String -> String md5String s = md5s (Str s) md5File :: String -> IO String md5File name = do h <- openFile name ReadMode s <- hGetContents h hClose h return (md5s (Str s)) -- | Remove any trailing strings matcing /irs/ (input record separator) -- from input string. Like perl's chomp(1). -- chomp :: String -> String -> String chomp irs st | irs `isSuffixOf` st = let st' = reverse $ drop (length irs) (reverse st) in chomp irs st' | otherwise = st {-# INLINE chomp #-} -- -- | Split a list into pieces that were held together by glue. Example: -- -- > split ", " "one, two, three" ===> ["one","two","three"] -- split :: Eq a => [a] -- ^ Glue that holds pieces together -> [a] -- ^ List to break into pieces -> [[a]] -- ^ Result: list of pieces split glue xs = split' xs where split' [] = [] split' xs' = piece : split' (dropGlue rest) where (piece, rest) = breakOnGlue glue xs' dropGlue = drop (length glue) {-# INLINE split #-} -- -- | Break off the first piece of a list held together by glue, -- leaving the glue attached to the remainder of the list. Example: -- Like break, but works with a [a] match. -- -- > breakOnGlue ", " "one, two, three" ===> ("one", ", two, three") -- breakOnGlue :: (Eq a) => [a] -- ^ Glue that holds pieces together -> [a] -- ^ List from which to break off a piece -> ([a],[a]) -- ^ Result: (first piece, glue ++ rest of list) breakOnGlue _ [] = ([],[]) breakOnGlue glue rest@(x:xs) | glue `isPrefixOf` rest = ([], rest) | otherwise = (x:piece, rest') where (piece, rest') = breakOnGlue glue xs {-# INLINE breakOnGlue #-} -- | Reverse cons. Add an element to the back of a list. Example: -- -- > snoc 3 [2, 1] ===> [2, 1, 3] snoc :: a -- ^ Element to be added -> [a] -- ^ List to add to -> [a] -- ^ Result: List ++ [Element] snoc x xs = xs ++ [x] -- | Join lists with the given glue elements. Example: -- -- > joinWith ", " ["one","two","three"] ===> "one, two, three" joinWith :: [a] -- ^ Glue to join with -> [[a]] -- ^ Elements to glue together -> [a] -- ^ Result: glued-together list joinWith glue xs = (concat . intersperse glue) xs -- | 'dropSpace' takes as input a String and strips spaces from the -- prefix as well as the postfix of the String. Example: -- -- > dropSpace " abc " ===> "abc" dropSpace :: [Char] -> [Char] dropSpace = let f = reverse . dropWhile isSpace in f . f -- | untab an string expandTab :: String -> String expandTab [] = [] expandTab ('\t':xs) = ' ':' ':' ':' ':' ':' ':' ':' ':expandTab xs expandTab (x:xs) = x : expandTab xs -- | 'getRandItem' takes as input a list and a random number generator. It -- then returns a random element from the list, paired with the altered -- state of the RNG getRandItem :: (RandomGen g) => [a] -- ^ The list to pick a random item from -> g -- ^ The RNG to use -> (a, g) -- ^ A pair of the item, and the new RNG seed getRandItem [] _ = error "getRandItem: empty list" getRandItem mylist rng = (mylist !! index,newRng) where llen = length mylist (index, newRng) = randomR (0,llen - 1) rng -- | 'stdGetRandItem' is the specialization of 'getRandItem' to the standard -- RNG embedded within the IO monad. The advantage of using this is that -- you use the Operating Systems provided RNG instead of rolling your own -- and the state of the RNG is hidden, so one don't need to pass it -- explicitly. stdGetRandItem :: [a] -> IO a stdGetRandItem lst = getStdRandom $ getRandItem lst -- Local Variables: ** -- indent-tabs-mode: nil ** -- tab-width: 4 ** -- End: **