从Haskell中的(前序)位串重构Huffman树

从Haskell中的(前序)位串重构Huffman树,haskell,recursion,encoding,tree,huffman-code,Haskell,Recursion,Encoding,Tree,Huffman Code,我有以下Haskell多态数据类型: data Tree a = Leaf Int a | Node Int (Tree a) (Tree a) 树将被压缩为0和1的位字符串。“0”表示一个节点,其后是左子树的编码,然后是右子树的编码。“1”表示一片叶子,后跟7位信息(例如,它可能是一个字符)。每个节点/叶也应该包含存储信息的频率,但这对于这个问题并不重要(因此我们可以将任何内容放在那里) 例如,从这个编码树开始 [0,0,0,1,1,1,0,1,0,1,1,1,1,1,1,0,1,0,0,0

我有以下Haskell多态数据类型:

data Tree a = Leaf Int a | Node Int (Tree a) (Tree a)
树将被压缩为0和1的位字符串。“0”表示一个节点,其后是左子树的编码,然后是右子树的编码。“1”表示一片叶子,后跟7位信息(例如,它可能是一个字符)。每个节点/叶也应该包含存储信息的频率,但这对于这个问题并不重要(因此我们可以将任何内容放在那里)

例如,从这个编码树开始

[0,0,0,1,1,1,0,1,0,1,1,1,1,1,1,0,1,0,0,0,0,1,1,1,1,0,0,0,1,1,1,
 1,0,0,1,1,1,1,1,1,1,0,0,1,0,0,1,1,1,1,0,1,1,1,1,1,1,0,0,0,0,1]
它应该会回报像这样的东西

Node 0 (Node 0 (Node 0 (Leaf 0 'k') (Leaf 0 't')) 
       (Node 0 (Node 0 (Leaf 0 'q') (Leaf 0 'g')) (Leaf 0 'r'))) 
(Node 0 (Leaf 0 'w') (Leaf 0 'a'))
(间距不重要,但它不适合一行)

我几乎没有使用树的经验,尤其是在实现代码时。我对如何在纸上解决这个问题有一个模糊的想法(使用类似于堆栈的东西来处理深度/级别),但我仍然有点迷茫

任何帮助或想法都将不胜感激

向右折叠:

import Data.Char (chr)

data Tree a = Leaf a | Node (Tree a) (Tree a)
  deriving Show

build :: [Int] -> [Tree Char]
build xs = foldr go (\_ _ -> []) xs 0 0
  where
  nil = Leaf '?'
  go 0 run 0 0 = case run 0 0 of
    []     -> Node nil nil:[]
    x:[]   -> Node x   nil:[]
    x:y:zs -> Node x   y  :zs

  go 1 run 0 0 = run 0 1
  go _ _   _ 0 = error "this should not happen!"
  go x run v 7 = (Leaf $ chr (v * 2 + x)): run 0 0
  go x run v k = run (v * 2 + x) (k + 1)
然后:


好的,您正试图从一个位流解析一个字节树。解析是其中一种需要设置某种结构的情况:我们将以的样式编写一个小型解析器组合器库,这将允许我们以惯用的函数样式编写代码,并将大量工作委托给机器

翻译成monad transformers的语言,并将“字符串”读作“位字符串”,我们有

解析器是一种一元计算,它在布尔值流上有状态地运行,生成成功解析的
A
s的集合。GHC的
GeneralizedNewtypeDeriving
superpowers允许我省略
Monad
等的样板实例

因此,我们的目标是编写一个
解析器(treesevenbits)
——一个返回布尔型七元组树的解析器。(你可以在闲暇时将7位转换成
Word8
,方法是使用
Tree
并使用
fmap
)我将使用以下
Tree
的定义,因为它更简单-我相信你可以找出如何使此代码适应你自己的目的

data Tree a = Leaf a | Node (Tree a) (Tree a) deriving Show

type SevenBits = (Bool, Bool, Bool, Bool, Bool, Bool, Bool)
下面是一个解析器,它试图使用输入流中的一个位,如果输入流为空则会失败:

one :: Parser Bool
one = Parser $ do
    stream <- get
    case stream of
        [] -> empty
        (x:xs) -> put xs *> return x
现在我们终于可以编写解析树结构本身的代码了。我们将使用在
节点
选项之间进行选择

好的,这里有一个简单的(特别的,但更容易理解)方法

我们需要构建一个函数
parse
,其类型如下:

parse  :: [Int] -> Tree Char
您提到的使用堆栈的方法是必不可少的。这里我们只讨论递归调用。堆栈将由编译器构建,它将只存储每个递归调用(如果您愿意,至少您可以这样想象,或者忽略这一段)

因此,其思想如下:每当您找到
0
,您都需要对算法进行两次递归调用。第一个递归调用将读取树的一个分支(左分支)。第二个需要调用,列表的其余部分作为参数。其余的由第一个递归调用留下。因此,我们需要一个具有以下类型的辅助函数
parse'
(现在我们返回一对,作为列表其余部分的第二个值):

接下来,您可以看到一段代码,其中
0
的情况与前面描述的一样。
对于
1
的情况,我们只需要将接下来的7个数字以某种方式转换成一个字符(我将
toChar
的定义留给您),然后返回一个
Leaf
和列表的其余部分

parse' (0:xs) = let (l, xs')    = parse' xs
                    (r, xs'')   = parse' xs' in (Node 0 l r, xs'') --xs'' should be []
parse' (1:xs) = let w = toChar (take 7 xs) in (Leaf 0 w , drop 7 xs)
最后,我们的parse函数只调用辅助parse one并返回该对的第一个元素

parse xs = fst $ parse' xs

谢谢你非常详细的回答!然而,我认为这个解决方案超出了本练习的范围,目前我对Haskell的了解也不在此限。不过,当我对Haskell的更高级的主题更为熟悉时,我会回到这里(也欢迎您就如何在介绍课程之外改进函数式编程提出任何建议!)@David非常乐意回答您关于我的代码的任何问题!我最喜欢的介绍哈斯克尔的书是你可以在线免费阅读的-它可读性强,有趣,它包含了一个可访问的monads&co.的展览。谢谢你的解决方案!然而,到目前为止,我只使用了一些基本的折叠,我对您的解决方案的工作原理有点迷茫。我非常希望能有进一步的解释(特别是关于“去”和“跑”,因为这些名字并不是很有启发性,而且这些观点也很有说服力),这正是我一直在寻找的!这是一个优雅而简洁的解决方案。我一直在考虑以某种方式递归地计算左分支和右分支,但不知道如何将适当的列表输入到右分支的调用中。两人一组返回列表是个聪明的主意!值得一提的是,这本质上是一个写出来的
State
monad版本。是的,它实际上是一个
State
monad。更准确地说,它是一个在
[Int]
上工作的
解析器。
sevenBits :: Parser SevenBits
sevenBits = pack7 <$> replicateM 7 one
    where pack7 [a,b,c,d,e,f,g] = (a, b, c, d, e, f, g)
tree :: Parser (Tree SevenBits)
tree = node <|> leaf
    where node = bit False *> liftA2 Node tree tree
          leaf = bit True *> fmap Leaf sevenBits
ghci> runParser tree $ map (>0) [0, 1, 0,0,0,0,0,0,0, 1, 0,0,0,0,0,0,1]
[(Node (Leaf (False, False, False, False, False, False, False)) (Leaf (False, False, False, False, False, False, True)),[])]
parse  :: [Int] -> Tree Char
parse' :: [Int] -> (Tree Char, [Int])
parse' (0:xs) = let (l, xs')    = parse' xs
                    (r, xs'')   = parse' xs' in (Node 0 l r, xs'') --xs'' should be []
parse' (1:xs) = let w = toChar (take 7 xs) in (Leaf 0 w , drop 7 xs)
parse xs = fst $ parse' xs