Re: [Haskell] Кодирование методом Шеннона-Фано
От: lomeo Россия http://lomeo.livejournal.com/
Дата: 18.09.07 09:09
Оценка: 7 (2)
Здравствуйте, kurtx, Вы писали:

Делаю как на wiki:

Сначала пишу функцию деления списка на две части. Для начала опишу для удобства несколько типов.

data BinTree a = Leaf a | Branch (BinTree a) (BinTree a) deriving Show

type Freq = (Char, Integer)


Сама функция делит список на две части, суммы частот которых наиболее близки.

1. Для этого получаем сначала список всех возможных пар списков. Это можно сделать по разному (splitAt по всей длине или пройтись свёрткой, собирая списки). Мы сделаем просто — склеим головы и хвосты: zip (inits xs) (tails xs)

2. Отсортируем полученный список пар и выберем голову. sortBy в Haskell имеет тип (a -> a -> Ordering) -> [a] -> [a]. Т.к. у нас `a` — это ([(Char, Integer)], [(Char, Integer)]), то нам придётся сравнивать 4 списка (суммируем попарно, а потом берём разность), поэтому для упрощения напишем функцию сортировки, которая сравнивает значения получая их целые (Integer) представления:

sortAsInt :: (a -> Integer) -> [a] -> [a]
sortAsInt asInt xs = map fst $ sortBy (\(_, i) (_, j) -> i `compare` j) $ map (\xs -> (xs, asInt xs)) xs


Я предпочитаю лямбды не выписывать, поэтому в конечном коде их перенесу в where.

3. Собственно сама функция получения разницы сумм:

sumDiff xs ys = abs $ sndSum xs - sndSum ys
    where
            sndSum = sum . map snd


snd здесь нужно потому что у нас элемент xs (или ys) — это пара (Пара_списков, Разница_их_сумм)
Блин, быстрее пишется, чем объясняется.

Итого:

sortAsInt :: (a -> Integer) -> [a] -> [a]
sortAsInt asInt = map fst . sortBy sndCompare . map withInt
    where
        sndCompare (_, i) (_, j) = i `compare` j
        withInt xs = (xs, asInt xs)

divide :: [Freq] -> ([Freq], [Freq])
divide xs = head $ sortAsInt (uncurry sumDiff) $ zip (inits xs) (tails xs)
    where
        sndSum = sum . map snd
        sumDiff xs ys = abs $ sndSum xs - sndSum ys


Дальше генерация дерева. Это проще, т.к. генерация — это всего лишь рекурсивный обход — разделили на две части, для каждой части опять генерируем дерево и т.д. Если дошли до одиночного символа, генерируем из него листочек.

genTree :: [Freq] -> BinTree Freq
genTree [x] = Leaf x
genTree xs  = let (ls, rs) = divide xs in Branch (genTree ls) (genTree rs)
... << RSDN@Home 1.1.4 stable SR1 rev. 568>>
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.