Haskell如何将常规树转换为Data.Tree

时间:2013-05-09 12:12:05

标签: haskell tree type-conversion

我已将我的通用树声明如下

data GeneralTree a = EmptyTree | Node a [GeneralTree a]

我已经构建了这个GeneralTree的一个实例。现在我想将它转换为Data.Tree中声明的Tree类型。我该怎么做?

我想编写一个具有以下类型的函数

convertTree :: GeneralTree a -> Tree a

但是我在处理空树时遇到了麻烦,因为Tree的Data.Tree定义中没有对应的。

1 个答案:

答案 0 :(得分:3)

由于Maybe的定义,Tree a只能在树的顶层使用。这只会避免'纯'EmptyTree的转换,这听起来不错,因为目标类型中不能有等价的值。然后正如我在第一个回答中所说的那样,适应稍后出现EmptyTree的唯一方法是利用列表的线性递归特性,允许一种简单的方法来跳过它们。

最后,对于顶级节点,我们必须将结果包装为Maybe类型,以避免转换纯EmptyTree。然后,我们可以毫无顾虑地合理地在子林上工作。

这导致了这个版本,这是最容易遵循的。

convertGTree :: GeneralTree a -> Maybe (Tree a)
convertGTree EmptyTree             = Nothing
convertGTree (GNode a forestGTree) = Just (Node a forest)
    where 
      forest = buildForest forestGTree
      buildForest :: [GeneralTree a] -> [Tree a]
      buildForest []     = []
      buildForest (x:xs) = case x of  
        GNode n forest -> (Node n (buildForest forest)) : (buildForest xs) 
        _              -> buildForest xs

插图测试,

>>> let testGeneralTree = GNode "0" [GNode "1" [GNode "2" [], EmptyTree], GNode "3" [EmptyTree]]
>>> putStrLn . drawTree . fromJust . convertGTree $ testGeneralTree
0
|
+- 1
|  |
|  `- 2
|
`- 3

使用unfoldTree并返回一棵树(可能是a)

首先,避免将Node用于您的数据构造函数,因为它已经被Tree数据类型使用,否则您将遇到名称的冲突。这就是为什么我们选择像这样重新定义它,

data GeneralTree a = EmptyTree | GNode a [GeneralTree a] 
    deriving (Show)

您可以通过中间EmptyTree类型的平均值来处理此Maybe 首先,我传递一个buildSafeTree函数,该函数在场景后面使用unfoldTree 帮助管理案例并在遇到EmptyTree值时返回Nothing 构建安全树时,我们可以使用cleanSafeTree对其进行清理,诀窍在cleanForest中,它将按语句的大小跳过Nothing值。

以下代码,

testGeneralTree = GNode "0" [GNode "1" [GNode "2" [], EmptyTree], GNode "3" [EmptyTree]]

convertTree :: GeneralTree a -> Tree a
convertTree = cleanSafeTree . buildSafeTree

buildSafeTree :: GeneralTree a -> Tree (Maybe a)
buildSafeTree = 
  unfoldTree helper 
    where 
      helper :: GeneralTree a -> (Maybe a, [GeneralTree a]) 
      helper EmptyTree       = (Nothing, []) 
      helper (GNode a trees) = (Just a, trees)


cleanSafeTree :: Tree (Maybe a) -> Tree a 
cleanSafeTree tree@(Node (Just root) forest) = 
  Node root (cleanForest forest) 
    where 
      cleanForest :: Forest (Maybe a) -> Forest a
      cleanForest []     = []
      cleanForest (x:xs) = case x of  
        Node Nothing _      -> cleanForest xs
        Node (Just x) trees -> (Node x (cleanForest trees)) : (cleanForest xs) 

一些测试,

>>> testGeneralTree 
GNode "0" [GNode "1" [GNode "2" [],EmptyTree],GNode "3" [EmptyTree]]

>>> putStrLn . drawTree .  convertTree $ testGeneralTree
0
|
+- 1
|  |
|  `- 2
|
`- 3

>>> putStrLn . drawTree . fmap show . buildSafeTree $ testGeneralTree
Just "0"
|
+- Just "1"
|  |
|  +- Just "2"
|  |
|  `- Nothing
|
`- Just "3"
   |
   `- Nothing

使用unfoldForest的时间更短。

convertTrees :: GeneralTree a -> Tree a
convertTrees (GNode a forest) = 
  Node a (cleanForest . unfoldForest helper $ forest) 
    where
      -- build a Forest of Tree (Maybe a) from GeneralTree a
      toTreeMaybe :: GeneralTree a -> (Maybe a, [GeneralTree a])
      toTreeMaybe EmptyTree           = (Nothing, [])
      toTreeMaybe (GNode a subForest) = (Just a , subForest)
      -- Clean the Maybe a, for a Forest of Tree Maybe a
      fromMaybe :: Forest (Maybe a) -> Forest a
      fromMaybe []     = []
      fromMaybe (x:xs) = case x of    
        Node Nothing _     -> cleanForest xs
        Node (Just x) trees -> (Node x (cleanForest trees)) : (cleanForest xs) 

Monadic版本使用UnfoldTreeM返回Maybe (Tree a)

使用unfoldTreeMonadic

的解决方案
convertMonadicTrees :: GeneralTree a -> Maybe (Tree a)
convertMonadicTrees = unfoldTreeM helper where 
    helper :: GeneralTree a -> Maybe (a, [GeneralTree a]) 
    helper EmptyTree       = Nothing
    helper (GNode a trees) = Just (a, trees)

主要区别是,如果一个GeneralTree包含一个EmptyTree值,那么它将返回任何内容,它将被丢弃。

>>> testGeneralTree2 
GNode 0 [GNode 1 [GNode 2 [],EmptyTree],GNode 3 [EmptyTree]]
>>> convertMonadicTrees testGeneralTree2
Nothing