我正在将哈夫曼编码算法重写为Haskell新手练习,我正在努力改造我使用以下技术序列化的树:
- 在深度优先预订中从根走树
- 遇到一个节点放0然后递归左边然后右边的孩子
- 遇到一个Leaf放1然后是符号字节
我的序列化代码:
serializeHtree :: (Ord a) => CodeDict a -> HTree a -> [Bit]
serializeHtree dict (Leaf a) = I : (myLpad $ dict M.! a)
serializeHtree dict (Branch l r) = O : (serializeHtree dict r ++ serializeHtree dict l)
其中:
- CodeDict是从a到[Bit]的映射
- myLpad用0填充可变长度的霍夫曼代码,使其成为固定长度的
- 和Bit是我自己的数据类型由O和I构建
上面的树将表示为:
01的 00000000 001的 00000100 1的 00000101 01的 00000110 1的 00000111
现在反序列化它,我知道我必须每位读取流位并且:
- 遇到一个1用下一个字节制作一个叶子
- 遇到一个0使分支在左边和右边的子树上递归
但是我的方法失败了,这是我的反序列化代码(这次不起作用):
deserializeHtree :: [Bit] -> HTree a
deserializeHtree (x:xs) = case x of
'O' -> Branch (deserializeHtree xs) (deserializeHtree xs)
'I' -> Leaf (head xs)
感谢您的支持
答案 0 :(得分:2)
您需要使用适合递归的类型编写解析器。
在顶级,您确实需要[Bit] -> HTree a
(可能会将a
限制为某个类型类,但我忽略了这一点。但是,要启用递归,您需要
parser :: [Bit] -> (HTree a, [Bit])
-- or, if you need to handle failure
parser :: [Bit] -> Maybe (HTree a, [Bit])
这个想法是parser
被提供了要解析的位。
然后它尝试解析第一个树,该树在这些位的前缀中表示。如果成功,则返回HTree a
和超出(非消耗)位的列表。
返回非消耗位对于需要解析两个子树的Branch
至关重要。解析第一个,取非消耗的位,然后使用它们启动右子树的解析器。
这个主题对于SO答案来说过于宽泛,但是如果你谷歌为#34; Haskell解析器monad"你应该找到很多例子。