繁体   English   中英

Eq和Ord实例不一致?

[英]Inconsistent Eq and Ord instances?

我有一个很大的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库出现问题吗? 有没有其他方法可以满足详细的平等操作的要求,但便宜的订购?

首先,使用TextByteString而不是String可以帮助很多而不改变其他任何东西。

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


如果您根本不需要Eq实例,则可以简单地定义

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

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


如果你需要一个Eq来比较各个领域的实例,那么你就可以留在包装一致BigThingnewtype ,确定上述EqOrd它,并在你的算法使用它时,你需要根据订货name

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')

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

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情况下)。

module HashOrd
    ( Hashed()
    , getHashed
    , hashedHash
    )
where

import Data.Hashable

data Hashed a = Hashed { hashedHash :: Int, getHashed :: a }
  deriving (Ord, Eq, Show, Read, Bounded)

hashed :: (Hashable a) => a -> Hashed a
hashed x = Hashed (hash x) x

instance Hashable a => Hashable (Hashed a) where
    hashWithSalt salt (Hashed _ x) = hashWithSalt salt x

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM