我已将我的通用树声明如下
data GeneralTree a = EmptyTree | Node a [GeneralTree a]
我已经构建了这个GeneralTree的一个实例。现在我想将它转换为Data.Tree中声明的Tree类型。我该怎么做?
我想编写一个具有以下类型的函数
convertTree :: GeneralTree a -> Tree a
但是我在处理空树时遇到了麻烦,因为Tree的Data.Tree定义中没有对应的。
答案 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
首先,避免将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)
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