Haskell Quiz/Text Munger/Solution Tel

From HaskellWiki
< Haskell Quiz‎ | Text Munger
Revision as of 02:11, 9 October 2007 by Tel (talk | contribs)
Jump to navigation Jump to search


A somewhat naive solution using State transformers to pass StdGen through the process. Doesn't handle punctuation correctly.

{-# OPTIONS -fglasgow-exts #-}

module Main where

import System.Random
import Data.Ord (comparing)
import Data.List (sortBy)
import Control.Monad.State.Lazy

main :: IO ()
main = getStdGen >>= evalStateT loop

loop :: (MonadState StdGen m, MonadIO m) => m ()
loop = do l <- liftIO getLine
          munge <- mungeLine l
          liftIO $ putStrLn munge
          loop

mungeLine :: MonadState StdGen m => String -> m String
mungeLine = liftM unwords . mapM mungeWord . words

mungeWord :: MonadState StdGen m => String -> m String
mungeWord str = do gen <- splitST
                   let (randPairs :: [(Int, Char)]) = evalState (zipWithRand (,) mid) gen
                   return (first ++ map snd (sortBy (comparing fst) randPairs) ++ last)
    where (first, mid, last) = splitEnds str

splitEnds :: Eq a => [a] -> ([a], [a], [a])
splitEnds (x:[]) = ([x], [], [])
splitEnds xs = ([head xs], (init . tail) xs, [last xs]) 

-- zip across a list, passing a (Random t => t) in
zipWithRand :: (Random a, MonadState StdGen m) => (a -> b -> c) -> [b] -> m [c]
zipWithRand fn xs = do rands <- sequence $ map (const randomST) xs 
                       return $ zipWith fn rands xs

-- random promoted inside some MonadState for threading the StdGen
randomST :: (MonadState StdGen m, Random a) => m a
randomST = do gen <- get
              let (x, gen2) = random gen
              put gen2
              return x

-- split promoted inside some MonadState for generating new a StdGen
splitST :: MonadState StdGen m => m StdGen
splitST = do gen <- get
             let (newgen, gen2) = split gen
             put gen2
             return newgen