haskell二叉树函数

haskell二叉树函数,haskell,binary-tree,Haskell,Binary Tree,我必须用haskell编写一个函数来检查两个二叉树是否是彼此的镜像。这是我到目前为止所拥有的,但我不知道True&&areMirrorImages l1 r2&&areMirrorImages r1 l2是否是正确的方法。我试图用递归调用areMirrorImages函数的结果来验证逻辑性和真实性 -- Tests whether two BinaryTrees are mirror images of one another areMirrorImages :: (Eq (BinaryTree

我必须用haskell编写一个函数来检查两个二叉树是否是彼此的镜像。这是我到目前为止所拥有的,但我不知道True&&areMirrorImages l1 r2&&areMirrorImages r1 l2是否是正确的方法。我试图用递归调用areMirrorImages函数的结果来验证逻辑性和真实性

-- Tests whether two BinaryTrees are mirror images of one another
areMirrorImages :: (Eq (BinaryTree a), Eq a) => BinaryTree a -> BinaryTree a -> Bool
areMirrorImages Empty Empty = True
areMirrorImages _ Empty = False
areMirrorImages Empty _     = False
areMirrorImages (Node x1 left1 right1) (Node x2 left2 right2) = 
    if (x1 == x2 && left1 == right2 && right1 == left2)
then True && (areMirrorImages left1 right2) && (areMirrorImages right1 left2)
else False 
这是我尝试编译时遇到的错误:

A2.hs:2:0:
    Non-type variables, or repeated type variables,
      in the constraint: Eq (BinaryTree a)
    (Use -fglasgow-exts to permit this)
    In the type signature:
      areMirrorImages :: (Eq (BinaryTree a), Eq a) =>
                         BinaryTree a -> BinaryTree a -> Bool

如果BinaryTree有一个Eq的派生实例,我似乎无法找出问题所在

可能您需要的唯一约束是Eq a。该派生实例将类似于:

instance Eq a => Eq (BinaryTree a) where
    ...
因此,知道可以比较
a
是否相等,Haskell就会发现可以比较
BinaryTree a
值是否相等

你的逻辑在我看来也可能是错误的。例如,除非
left1==right2
都是镜像,否则函数将返回False。除非left1和right2是对称的(假设aremrrorImages的实现是正确的),否则这两个参数不可能都是真的

需要
Eq(二进制树a)
意味着您需要比较(测试
Eq
a)两棵树。您不需要这样做,因为您正在测试树是否是彼此的镜像,而不是它们是否相等

树木什么时候是彼此的镜子?当键相同且相反的分支彼此镜像时

键是相同的:
x1==x2

相反的分支是镜像:
areMirrorImages left1 right2&&areMirrorImages right1 left2

将其转化为Haskell:

areMirrorImages (Node x1 left1 right1) (Node x2 left2 right2) = 
    x1 == x2 && areMirrorImages left1 right2 && areMirrorImages right1 left2

Haskell允许您编写简洁、直接的代码。没有必要使用
if-then-else

您的想法是正确的。我已经将您的版本稍微精简了一点,它似乎工作得很好:

data BinaryTree a = Empty | Node a (BinaryTree a) (BinaryTree a)
                  deriving(Show, Eq)

areMirrorImages :: (Eq a) => BinaryTree a -> BinaryTree a -> Bool
areMirrorImages (Node x1 left1 right1) (Node x2 left2 right2) 
    | x1 == x2  = areMirrorImages left1 right2 &&
                  areMirrorImages right1 left2
    | otherwise = False
areMirrorImages Empty Empty = True
areMirrorImages _ _ = False
那么我改变了什么

类型签名-最后,您只需要在树的元素上调用==即可,因此只有
a
需要是
Eq
的实例。(即使我在数据声明中推导了
Eq

条件-就像我说的,最后你只需要测试特定元素是否相等。检查
left1==right2&&right1==left2
没有意义,因为它们不应该相等,所以这些分支应该是彼此的镜像

守卫而不是if——我认为守卫比if更漂亮

模式匹配-只在结尾处设置catch all False稍微干净一些(imho)

我最近一直在尝试学习QuickCheck,所以我也写出了这段代码来证明areMirrorImages是有效的

instance (Arbitrary a) => Arbitrary (BinaryTree a) where
    arbitrary = fromList `fmap` arbitrary

fromList :: [a] -> BinaryTree a
fromList []     = Empty
fromList (x:xs) = Node x (fromList lhalf) (fromList rhalf)
    where (lhalf, rhalf) = splitAt (length xs `div` 2) xs

mirror :: BinaryTree a -> BinaryTree a
mirror Empty = Empty
mirror (Node x l r) = Node x (mirror r) (mirror l)

prop_mirror tree = areMirrorImages tree (mirror tree)
我对
BinaryTree
arbitral
实例不是最好的;它只能制造出近乎平衡的树木。但我觉得它相当不错。还请注意,
prop_mirror
仅测试
何时为镜像图像
应返回
True
;它不会暴露潜在的误报(尽管我相当肯定不会有)


我建议您编写一个普通的(未镜像的)相等函数和第二个镜像树的函数,然后说
isMirrorOf x y=x==mirror y
?这样编写代码往往更清晰。
True&&expr
是不必要的;它将始终计算为
expr
,因此您可以直接删除
True&&
。另请参阅注意,使用
mirror
的此定义,您可以按照barsoap的建议编写
areMirrorImages x y=x==mirror y
。(这将使
prop\u mirror
变得毫无价值。)…但您可以添加
prop\u mirror x=x==(mirror.mirror)x
。与流行的观点相反,无限并不是你在面对面的两面镜子中看到的东西。
*Main> quickCheck prop_mirror 
+++ OK, passed 100 tests.