Я представляю себе такую функцию, как
takeChunkUntil :: [a] -> ([a] -> Bool) -> ([a], [a])
Надеюсь, ленивый.
Он берет элементы из первого списка, пока группа их не удовлетворяет предикату,затем возвращает этот подсписок, а также остальные элементы.
ОТВЕТИТЬ НА НЕКОТОРЫЕ ВОПРОСЫ:
Конечная цель - сделать что-то, что читает коды Хаффмана лениво. Поэтому, если у вас есть строка битов, представленная здесь как Bool, bs
, вы можете написать take n $ decode huffmanTree bs
, чтобы получить первые n кодированных значений, потребляя при этом только столько bs
, сколько необходимо. Если вы хотите, я опубликую более подробную информацию и мои попытки решения. Это может занять много времени :) (Обратите внимание, что я учитель, которому эта проблема была задана студентом, но я не пытался помочь ему, поскольку это было выше моего понимания, однако мне сейчас очень любопытно.)
Продолжение: Вот и все:
Определение дерева Хаффмана:
data BTree a = Leaf a | Fork (BTree a) (BTree a) deriving (Show, Eq)
Цель: написать функцию отложенного декодирования, которая возвращает пару декодированныхзначения и логическое значение, указывающее, остались ли какие-либо значения, которые не были достаточно длинными, чтобы их можно было декодировать в значение. Примечание: мы используем Bool для представления бита: True = 1, False = 0.
decode :: BTree a -> [Bool] -> ([a], Bool)
Вот суть: первая функция, которую я написал, была функцией, которая декодирует одно значение. Ничего не возвращает, если входной список был пуст, в противном случае возвращает декодированное значение и оставшийся «бит».
decode1 :: BTree a -> [Bool] -> Maybe (a, [Bool])
decode1 (Leaf v) bs = Just (v, bs)
decode1 _ [] = Nothing
decode1 (Fork left right) (b:bs)
| b = decode1 right bs
| otherwise = decode1 left bs
Сначала я решил, что мне нужна какая-то хвостовая рекурсия, чтобы сделать это ленивым. Вот что не не работает. Во всяком случае, я думаю, что нет. Обратите внимание, как это рекурсивно, но я передаю список «символов, декодированных до сих пор» и добавляю новый. Неэффективно и, возможно, (если мое понимание верно) не приведет к хвостовой рекурсии.
decodeHelp :: BTree a -> [a] -> [Bool] -> ([a],Bool)
decodeHelp t symSoFar bs = case decode1 t bs of
Nothing -> (symSoFar,False)
Just (s,remain) -> decodeHelp t (symSoFar ++ [s]) remain
Поэтому я подумал, как мне написать лучшую рекурсию, в которой я декодирую символ и добавляю его кследующий звонок? Ключ должен вернуть список [Maybe a], в котором Just a
является успешно декодированным символом, а Nothing
означает, что ни один символ не может быть декодирован (т. Е. Оставшихся логических значений недостаточно)
decodeHelp2 :: BTree a -> [Bool] -> [Maybe a]
decodeHelp2 t bs = case decode1 t bs of
Nothing -> [Nothing]
Just (s, remain) -> case remain of
[] -> []
-- in the following line I can just cons Just s onto the
-- recursive call. My understand is that's what make tail
-- recursion work and lazy.
_ -> Just s : decodeHelp2 t remain
Но, очевидно, это не то, чего хочет набор проблем из результата. Как я могу превратить все эти [Maybe a]
в ([a], Bool)
? Моей первой мыслью было применить scanl
Вот функция сканирования. Он накапливает Maybe a
в ([a], Bool)
sFunc :: ([a], Bool) -> Maybe a -> ([a], Bool)
sFunc (xs, _) Nothing = (xs, False)
sFunc (xs, _) (Just x) = (xs ++ [x], True)
Тогда вы можете написать
decodeSortOf :: BTree a -> [Bool] -> [([a], Bool)]
decodeSortOf t bs = scanl sFunc ([],True) (decodeHelp2 t bs)
Я проверил это работает и ленив:
take 3 $ decodeSortOf xyz_code [True,False,True,True,False,False,False,error "foo"]
дает [("",True),("y",True),("yz",True)]
Но это не желаемый результат. Помогите, я застрял!