我的任务是从这种数据类型的二叉树中获取所有叶子:
data Bintree el = Empty |
Tree {left :: Bintree el, node :: el, right :: Bintree el} deriving Show
现在,我有了这个Treefold函数,可以在测试中完美运行:
binTreeFold :: (a -> b -> a -> a) -> a -> Bintree b -> a
binTreeFold f el Empty = el
binTreeFold f el tree@(Tree leftTree node rightTree) = f (binTreeFold f el leftTree) node (binTreeFold f el rightTree)
我尝试获取所有叶子,但是以某种方式根本无法正常工作(下面提到的编译问题):
leavesWithFold :: Bintree a -> [a]
leavesWithFold Empty = []
leavesWithFold tree = binTreeFold (\left -> \node -> \right -> if (checkIfTreeIsLeaf node) then left ++ [node] ++ right else left ++ [] ++ right) [] tree
使用以下功能:
checkIfTreeIsLeaf :: Bintree a -> Bool
checkIfTreeIsLeaf Empty = False
checkIfTreeIsLeaf (Tree Empty node Empty) = True
checkIfTreeIsLeaf _ = False
编译器告诉我
Couldn't match type `a' with `Bintree a0'
但是在以前的功能中,使用
left ++ [node] ++ right
工作正常。你知道我做错了什么,也许我可以改变什么?
最好的愿望
答案 0 :(得分:2)
通常,使用fold
函数的想法是它可以为您完成所有工作。因此,通过编写分支为Empty
和tree
大小写的函数,这已经有点奇怪了。
但是让我们简单地从递归方法开始。暂时不要折叠等。我们一般如何生成叶子列表?
这里基本上有三种情况:
如果遇到Empty
,则返回空列表,因为Empty
不是 一片叶子,因此应该返回所有叶子,所以:
leaves Empty = []
如果我们遇到一个Tree
作为左子树和右子树的Empty
,我们知道Tree
是叶子,因此我们返回它携带的元素,作为列表:
leaves (Tree Empty x Empty) = [x]
如果两个子树之一(或两者都不是)Empty
,我们将递归调用leaves
函数,并将两个子树连接在一起:
leaves (Tree l _ r) = leaves l ++ leaves r
所以现在我们得到了:
leaves :: Bintree a -> [a]
leaves Empty = []
leaves (Tree Empty x Empty) = [x]
leaves (Tree l x r) = leaves l ++ leaves r
我们当然无法使用binTreeFold
方法做到这一点。但是我们不能检测树木是否实际上是叶子吗?如果两个递归调用都导致一个空列表,我们知道当前的Bintree
实际上是一片叶子。确实:如果左(和右)子树为Empty
,则它们将导致空列表。并且在这些不为空的情况下,它们要么是叶子,将递归地生成单例列表,要么不是带有后代的Tree
,最终将导致叶子,因此导致非空列表。
因此,我们首先看一下递归调用,然后基于以下事实:它们是否都是空列表,要么返回具有leaf元素的单例列表,要么将两个列表连接起来在一起,就像:
f [] x [] = [x]
f l _ r = l ++ r
或者在binTreeFold
函数中使用它:
leavesWithFold :: Bintree a -> [a]
leavesWithFold = binTreeFold f []
where f [] x [] = [x]
f l _ r = l ++ r