接受的答案在我看来是错误的,因为您说要根据树的概念内容而不是它们的确切结构进行比较。也就是说,您希望以下内容为真:
K 1 L (K 2 L L) == K 2 (K 1 L L) L
两个 BST 具有相同的两个元素,顺序正确但结构不同。在结构上比较它们,如 severij 的回答,产生False,但你希望它是True。
那么,如何按内容而不是按结构进行比较?好吧,任何两个具有相同项目集的 BST 将具有相同的中序遍历。因此,您可以完全按照您在问题中所说的那样做:将它们转换为列表,然后比较列表。但是您不必对它们进行排序:因为它们是 BST,所以可以保证您可以以相同的顺序遍历它们。并且将它们转换为列表并不是非常昂贵,所以不用担心:懒惰和流融合使其成本与手动编写遍历大致相同。
如果您的树类型有一个可折叠的实例,这将容易得多,这对树来说无论如何都是一件好事。定义Foldable只需要定义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 定义 Eq:
instance 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 为您派生 Foldable 就像派生 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