我必须在haskell中编写一个函数来检查两个二叉树是否是彼此的镜像。这是我到目前为止,但我不知道是真的&& areMirrorImages l1 r2&& areMirrorImages r1 l2是正确的方法。我试图用递归调用areMirrorImages函数的结果来逻辑AND True。
-- 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
我似乎无法弄清楚出了什么问题
答案 0 :(得分:5)
要求Eq (BinaryTree a)
表示您需要比较(测试Eq
uality)两棵树。您不需要这样做,因为您正在测试树是否是彼此的镜像,而不是它们是否相等。
树木什么时候互相镜像?当键是相同的并且相对的分支是彼此的镜像时。
关键是相同的: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
。
答案 1 :(得分:1)
如果BinaryTree具有Eq的派生实例,那么您可能需要的唯一约束是Eq a。派生的实例看起来像:
instance Eq a => Eq (BinaryTree a) where
...
因此,知道可以比较a
的相等性将允许Haskell确定可以比较您的BinaryTree a
值是否相等。
你的逻辑看起来对我来说也许是错的。例如,除非 left1 == right2
和areMirrorImages left1 right2
都为True,否则您的函数将返回False。假设isMirrorImages的正确实现,除非left1和right2是对称的,否则这些都不可能都是真的。
答案 2 :(得分:0)
你有正确的想法。我已经将你的版本缩减了一点,看起来效果很好:
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
需要是{{1}的实例}。 (尽管我确实在数据声明中派生了Eq
)
条件 - 就像我说的那样,最后你只需要测试特定元素是否相等。检查Eq
是否有意义,因为它们不应该相等,这些分支应该是彼此的镜像。
警卫而不是if - 我认为警卫比ifs更漂亮。
模式匹配 - 最后只需要捕获所有False,这样会更加清晰(imho)。
我最近一直在努力学习QuickCheck,所以我也抽出了这段代码来证明areMirrorImages有效。
left1 == right2 && right1 == left2
我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)
Arbitrary
的实例不是最好的;它只能制造几乎平衡的树木。但我认为它非常好。另请注意,BinaryTree
仅测试prop_mirror
何时应返回areMirrorImages
;它没有暴露潜在的误报(尽管我很确定没有)。
True