Eq和Ord实例不一致?

时间:2013-06-14 18:19:55

标签: haskell typeclass

我有一个很大的Haskell程序,运行得非常慢。分析和测试表明,大部分时间花在比较非常重要的特定大型数据类型的相等和排序上。 Equality是一个有用的操作(这是状态空间搜索,图搜索比树搜索更可取),但是我只需要这个类的Ord实例来使用Maps。所以我想做的就是说

instance Eq BigThing where
(==) b b' = name b == name b' &&
            firstPart b == firstPart b' &&
            secondPart b == secondPart b' &&
            {- ...and so on... -}

instance Ord BigThing where
compare b b' = compare (name b) (name b')

但由于不同对象的名称可能并不总是不同,因此根据==,两个BigThings可能不等的奇怪情况有所冒险,但比较它们会产生EQ。

这是否会导致Haskell库出现问题?有没有其他方法可以满足详细的平等操作的要求,但便宜的订购?

1 个答案:

答案 0 :(得分:15)

首先,使用TextByteString代替String可以帮助很多,而无需更改任何其他内容。

通常我不建议创建EqOrd不一致的实例。图书馆可以合理地依赖它,你永远不知道它会导致什么样的奇怪问题。 (例如,您确定 Map不使用EqOrd之间的关系吗?)


如果您根本不需要Eq实例,则只需定义

即可
instance Eq BigThing where
    x == y  =  compare x y == EQ

然后,平等将与比较保持一致。不要求相等的值必须使所有字段相等。


如果您需要一个比较所有字段的Eq实例,那么您可以通过将BigThing包装到newtype中来保持一致,定义上述Eq和{{1对于它,并在您需要根据Ord

进行排序时在算法中使用它
name

更新:由于您说任何订购都可以接受,因此您可以使用散列优势。为此,您可以使用hashable包。我们的想法是您在数据创建时预先计算哈希值,并在比较值时使用它们。如果两个值不同,则几乎可以肯定它们的哈希值会有所不同,而且只比较它们的哈希值(两个整数),仅此而已。它看起来像这样:

newtype BigThing' a b c = BigThing' (BigThing a b c)
instance Eq BigThing' where
    x == y  =  compare x y == EQ
instance Ord BigThing' where
    compare (BigThing b) (BigThing b') = compare (name b) (name b')

请注意,使用此解决方案,排序看似随机,与字段没有明显关系。

您还可以将此解决方案与之前的解决方案结合使用。或者,您可以使用预先计算的哈希创建一个小模块来包装任何类型(包装的值必须与module BigThing ( BigThing() , bigThing , btHash, btName, btSurname ) where import Data.Hashable data BigThing = BigThing { btHash :: Int, btName :: String, btSurname :: String } -- etc deriving (Eq, Ord) -- Since the derived Eq/Ord instances compare fields lexicographically and -- btHash is the first, they'll compare the hash first and continue with the -- other fields only if the hashes are equal. -- See http://www.haskell.org/onlinereport/derived.html#sect10.1 -- -- Alternativelly, you can create similar Eq/Ord instances yourself, if for any -- reason you don't want the hash to be the first field. -- A smart constructor for creating instances. Your module will not export the -- BigThing constructor, it will export this function instead: bigThing :: String -> String -> BigThing bigThing nm snm = BigThing (hash (nm, snm)) nm snm 个实例一致Eq个实例。)

Hashable