Haskell:把一棵树变成一张地图

时间:2013-07-16 17:32:56

标签: haskell map binary-tree

基本上我想将BST树变成一个地图,其中节点是键,节点的出现次数是值。所以,如果我输入了这个:

  

toMap(叶子13)

我会得到

> [(13,1)]

这是我到目前为止所做的:

data Tree a = Empty | Node a (Tree a) (Tree a) deriving (Show)
leaf x = Node x Empty Empty

toMap' :: Int -> Tree a -> ([(a, Int)], Int)
toMap' a Empty = ([], a)
toMap' a (Node x xl xr) = ((x, a): xl' ++ xr', k)
                      where (xl', i) = toMap' (a+1) xl
                            (xr', k) = toMap' (i) xr

toMap :: Tree a -> [(a, Int)]
toMap = fst. toMap' 1

此程序返回地图,但值不正确。每个值都比前一个值大一个(因此如果有3个节点,则第三个节点的值将是3个)。我想我必须在每个新钥匙上放置一个计数器,但我不确定如何。提前致谢

2 个答案:

答案 0 :(得分:5)

假设你有一个跨树折叠的函数foldt(以某种顺序与你当前的应用程序无关),以及一些函数insertIncr,它插入或递增{{1}中一个键的值你可以将一个应用到另一个。

您将处理以下类型签名:

Map a Int

import Data.Map foldt :: (a -> b -> b) -> b -> Tree a -> b foldt f acc Empty = acc foldt f acc (Node x l r) = acc' where accl = foldt f acc l accr = foldt f accl r acc' = f x accr -- insert 1 if not present, increment if present insertIncr :: Ord a => a -> Map a Int -> Map a Int insertIncr = undefined toMap' :: Ord a => Tree a -> Map a Int toMap' = foldt insertIncr empty 功能可以使用例如Data.Map.insertWith。请注意,为了将某些内容插入到Data.Map中,必须使用insertIncr类型类。如果您更喜欢普通的Ord种地图,则[(a,Int)]可以使用insertIncr类型。

修改:将使用 adjustWithKey 的建议更改为 insertWith

答案 1 :(得分:2)

老实说,这是一个我只是通过构图分解来解决的案例。

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

toMap :: Ord a => Tree a -> [(a, Int)]
toMap = countDups . toList

请注意,我必须在a上添加额外的约束。它至少需要Eq才能解决,但Ord允许渐近更好的解决方案。

这里的基本想法是将解决方案分解为部分,然后逐步确定每个部分。所以,下一部分是toList。我不会认为订单很重要,因此我会选择一个前缀排序,因为很容易让它变得懒惰和简单。

toList :: Tree a -> [a]
toList Empty = []
toList (Node a l r) = a : toList l ++ toList r

好的,好的,直截了当的。计算重复数。让我们把它分解成几块。

countDups :: Ord a => [a] -> [(a, Int)]
countDups = map (\xs -> (head xs, length xs)) . group . sort

好的,我可能会利用group中的sortData.List轻微欺骗。但另一方面,这正是group要解决的问题。排序只是一切的标准工具。

如果我导入Control.Arrow,我会用(head &&& length)替换lambda。但这只是一个标准的习惯用法,并没有真正简化事情 - 它只是让它们更简洁一点。

这种方法的主要思想是将问题分解为自己做一些有意义的事情。然后将这些部分组合成一个完整的解决方案。有一种方法可以将Tree a转换为[a]。也许还有一个能够做到这一点的功能。一旦你这样做,剩下的部分是一个有用的逻辑,可用于处理列表。如果你打破了它,你会发现它是现有位列表功能的简单组合。

这通常是解决任何编程语言问题的最佳方法之一 - 将大任务分解为更小的任务。在Haskell中做这件事的好处在于将较小的任务组合成完整的过程是一个非常简洁的过程。