Paste: huffman in haskell
Author: | komu |
Mode: | literate-haskell |
Date: | Tue, 27 Oct 2009 07:16:36 |
Plain Text |
> import Control.Arrow ((&&&))
> import Data.List (group, sort, insert)
> import qualified Data.Map as M
> import Test.QuickCheck (quickCheck, (==>), Property)
> type BitString = [Int]
> type Freqs a = [(a,Int)]
> data FreqTree a = Leaf a Int
> | Branch (FreqTree a) (FreqTree a) Int
> deriving (Eq, Show)
> frequency :: FreqTree a -> Int
> frequency (Leaf _ f) = f
> frequency (Branch _ _ f) = f
> instance Eq a => Ord (FreqTree a) where
> compare l r = compare (frequency l) (frequency r)
> merge :: FreqTree a -> FreqTree a -> FreqTree a
> merge l r = Branch l r (frequency l + frequency r)
> frequencies :: Ord a => [a] -> Freqs a
> frequencies = map (head &&& length) . group . sort
> buildTree :: Ord a => Freqs a -> FreqTree a
> buildTree = normalize . build . sort . map (uncurry Leaf)
> where
> build [] = error "can't build tree without input"
> build [t] = t
> build (t1:t2:ts) = build (insert (merge t1 t2) ts)
> normalize t@(Branch _ _ _) = t
> normalize t@(Leaf _ _) = merge t t
> createDictionary :: Ord a => FreqTree a -> M.Map a BitString
> createDictionary = M.fromList . paths []
> where
> paths p (Leaf a _) = [(a,reverse p)]
> paths p (Branch l r _) = paths (0:p) l ++ paths (1:p) r
> encode :: Ord a => Freqs a -> [a] -> BitString
> encode d s = concatMap encoder s
> where
> encoder a = maybe (error "unknown input") id (M.lookup a dict)
> dict = createDictionary (buildTree d)
> decode :: Ord a => Freqs a -> BitString -> [a]
> decode d s = loop s tree
> where
> loop xs (Leaf a _) = a : loop xs tree
> loop (0:xs) (Branch l _ _) = loop xs l
> loop (1:xs) (Branch _ r _) = loop xs r
> loop [] _ = []
> loop _ _ = error "malformed bit-string"
> tree = buildTree d
> prop_decoding_is_inverse_for_decoding :: [Int] -> Property
> prop_decoding_is_inverse_for_decoding xs =
> not (null xs) ==> decode fs (encode fs xs) == xs
> where fs = frequencies xs
> test :: IO ()
> test = quickCheck prop_decoding_is_inverse_for_decoding
New Annotation