我有点坚持为二叉搜索树实现一个Eq实例。我想检查两棵树中的值是否相同,但我真的不知道如何正确地做到这一点。由于两个二叉搜索树的结构可能不同,我不能直接比较节点。我的想法是将它们转换为列表,对列表进行排序并对它们进行比较,但这看起来非常复杂,我想知道是否还有其他方法可以做到这一点。这是我到目前为止所得到的:
data BB a = L | K a (BB a) (BB a) deriving (Show)
instance Eq BB where
not (isBinarySearchTree t1 && isBinarySearchTree t2) = False
inOrder t1 == inOrder t2 = True
答案 0 :(得分:2)
接受的答案对我来说是错误的,因为你说你想根据树的概念内容而不是它们的确切结构进行比较。也就是说,您希望以下内容为真:
K 1 L (K 2 L L) == K 2 (K 1 L L) L
两个BST具有相同的两个元素,正确排序但结构不同。在结构上比较它们,如在severij的答案中,产生False
,但你希望它是True
。
那么,如何按内容而不是按结构进行比较?那么,具有相同项目集的任何两个BST将具有相同的有序遍历。因此,您可以完全按照您在问题中的说法进行操作:将它们转换为列表,并比较列表。但是你不必对它们进行排序:因为它们是BST,你可以保证你可以按照相同的顺序遍历它们。将它们转换为列表并不是非常昂贵,所以不要担心:懒惰和流融合会使其成本与手动编写遍历相同。
如果你的树类型有一个可折叠的实例,那将会容易得多,无论如何树都是一件好事。要定义可折叠,您只需要定义foldr
,这很容易:
import Prelude hiding (foldr)
import Data.Foldable (Foldable, foldr, toList)
instance Foldable BB where
foldr f init L = init
foldr f init (K x left right) = foldr f (f x (foldr f init right)) left
然后你可以用toList:
来定义Eqinstance Eq (BB a) where
x == y = toList x == toList y
之后,您可以根据需要
*Main> (K 1 L (K 2 L L)) == (K 2 (K 1 L L) L)
True
但这是相当多的工作,不是吗?在写这个答案时,我多次错误地定义了foldr
。事实证明,有一种方法可以避免自己编写:DeriveFoldable
语言扩展允许GHC为您导出可折叠,就像它派生Show一样容易。但由于它按顺序折叠数据类型中的字段,因此您必须稍微更改它,以便左子树出现在根节点之前:
{-# LANGUAGE DeriveFoldable #-}
import Prelude hiding (foldr)
import Data.Foldable (Foldable, foldr, toList)
data BB a = L | K (BB a) a (BB a) deriving (Show, Foldable)
instance Eq (BB a) where
x == y = toList x == toList y
事实上,我们仍然拥有所需的相等属性(尽管我们必须以不同方式编写节点,因为我们更改了字段顺序):
*Main> (K L 1 (K L 2 L)) == (K (K L 1 L) 2 L)
True
还有一个最后的改进:如果导入Data.Function.on
,Eq定义也可以更简单。你可以简单地用on
来比较两个树的相等性,将每个树转换成一个列表并比较这些列表是否相等"而不是手工拼写全部。让树定义和其他导入保持不变,我们终于获得了所需特征的非常简洁的定义,所有实际工作都是由已编写的库函数完成的:
{-# LANGUAGE DeriveFoldable #-}
import Prelude hiding (foldr)
import Data.Foldable (Foldable, foldr, toList)
import Data.Function (on)
data BB a = L | K (BB a) a (BB a) deriving (Show, Foldable)
instance Eq a => Eq (BB a) where
(==) = (==) `on` toList