霍夫曼树解码

时间:2019-10-26 04:09:22

标签: haskell recursion huffman-code

给出一个Huffman tree和一个比特流,返回一个包含(1) -由位编码的符号字符串(根据霍夫曼树),以及 -(2)一个布尔值,指示输出流是否包含来自 -输入(即,如果剩余任何位,则返回False。)

这是代码,它仅返回树中的第一个符号。有什么问题吗?

data BTree a = Leaf a | Fork (BTree a) (BTree a) deriving (Show, Eq)

traT :: BTree a -> BTree a -> [Bool] -> [a] -> ([a], Bool)
traT (Leaf v) c bs res= (res++[v], True)
traT (Fork left right) c (b:bs) res
   | b         = traT right c bs res
   | otherwise = traT left c bs res
traT _ c [] res = (res, True)
traT _ c bs res = traT c c (bs) res
traT _ c bs res = (res, False)

decode :: BTree a -> [Bool] -> ([a], Bool)
decode (Fork x y) bs = traT (Fork x y) (Fork x y) bs []
decode (Leaf x) bs = traT(Leaf x) (Leaf x) bs []

1 个答案:

答案 0 :(得分:2)

好吧,您似乎处在正确的轨道上。

  

它仅返回树中的第一个符号。

您的主要问题在于这两行:

traT (Leaf v) c bs res= (res++[v], True)
...
traT _ c bs res = traT c c (bs) res

第一个掩盖所有叶节点的第二个。第二个是您唯一可以在叶节点上运行的前向递归调用,因此您唯一的希望是处理任何其他位。

几句话:

  1. res++[v]表达式会强制代码在每个新符号处重新扫描整个符号列表。
  2. 第二行会不断地调用自己(但被第一行遮盖了)。

另一个(较小的)问题是,在位流的末尾仅返回一个标志以表示“额外”位的存在会丢失信息,因为我们想知道多余的位是什么。在您的核心递归函数中执行此操作有点冒险。当然,完全可以在最后的外部decode函数中进行操作。

这就是为什么在下面的代码示例中,我使用了额外的symBits参数来保留已处理但尚未归因于符号的位。我将它们保持相反的顺序,因为Haskell宁愿将项目放在列表的前面,而不是将它们放在最后,而是重新扫描整个列表。因此,在处理的最后阶段调用reverse。这是一个廉价的reverse调用,因为它的长度受到霍夫曼树深度的限制。

所以这是一些建议的重做代码,在这里我试图区分4种情况:叶节点或派生节点AND在位流的末尾与否。我还冒昧地将您的c参数重命名为htop。

data BTree a = Leaf a | Fork (BTree a) (BTree a)  deriving (Show, Eq)

type Bit = Bool


--            hnode   htop     symBits    bs
travHT :: BTree a -> BTree a -> [Bit] -> [Bit] -> ([a], [Bit])

-- situations where at least one input bit remains:
travHT (Leaf v) htop symBits (b:rbs) =  -- CHANGE: forward recursive call
                     -- symbol completed, jump from leaf node to top of htree:
                     let  fwdRes      = travHT htop htop [] (b:rbs)
                          nextSyms    = fst fwdRes
                          lastSymBits = snd fwdRes
                     in   (v : nextSyms, lastSymBits)
travHT (Fork left right) htop symBits (b:rbs)
   | b          = travHT right htop  (b:symBits)  rbs
   | otherwise  = travHT left htop   (b:symBits)  rbs

-- situations where we have reached the end of the bit stream:
travHT (Leaf v)           htop  symBits [] = ([v],[])
--   no more bits and not at a leaf --> incomplete last symbol:
travHT (Fork left right)  htop  symBits [] = ([], reverse symBits)

-- homework-mandated interface:
decode :: BTree a -> [Bit] -> ([a], Bool)
decode htree bs =
   let pair = travHT htree htree [] bs
       (symbols, restOfBits) = pair
       weUsedAllBits = null restOfBits
   in  (symbols, weUsedAllBits)

使用令牌主程序测试代码:

xyz_code :: BTree Char
xyz_code = Fork (Leaf 'x') (Fork (Leaf 'y') (Leaf 'z'))

-- Bit streams for test purposes:
------      Y           Z          X       X       X      Y/Z??
bl0 = [True,False,  True,True  , False,  False,  False]
bl1 = [True,False,  True,True  , False,  False,  False,  True]


main = do
    let bitList = bl0
    let htree   = xyz_code
    let result = decode  htree  bitList
    putStrLn $ "result = " ++ show result

程序输出:

result = ("yzxxx",True)

希望有帮助。我还将要求具有的功能将[huffman-code]标记添加到您的问题中。标签是帮助人们找到他们感兴趣的问题的好方法。而且我们确实有霍夫曼代码的标签。