Haskell Eq和Ord实例不一致?
我有一个大型Haskell程序,运行速度慢得令人沮丧。分析和测试表明,大部分时间都花在比较某个非常重要的特定大型数据类型的相等性和顺序上。相等是一个有用的操作(这是状态空间搜索,图形搜索比树搜索更可取),但为了使用映射,我只需要这个类的Ord实例。所以我想说的是Haskell Eq和Ord实例不一致?,haskell,typeclass,Haskell,Typeclass,我有一个大型Haskell程序,运行速度慢得令人沮丧。分析和测试表明,大部分时间都花在比较某个非常重要的特定大型数据类型的相等性和顺序上。相等是一个有用的操作(这是状态空间搜索,图形搜索比树搜索更可取),但为了使用映射,我只需要这个类的Ord实例。所以我想说的是 instance Eq BigThing where (==) b b' = name b == name b' && firstPart b == firstPart b' &&
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')
但是,由于不同对象的名称可能并不总是不同的,这就有可能出现一种奇怪的情况,即根据==,两个大对象可能是不相等的,但是比较它们会得到EQ
这会导致Haskell库出现问题吗?有没有其他方法可以满足对详细相等操作的要求,但需要便宜的订购 首先,使用
Text
或ByteString
而不是String
可以在不改变任何其他内容的情况下提供很大帮助
一般来说,我不建议创建与Ord
不一致的Eq
实例。图书馆完全可以依赖它,你永远不知道它会导致什么样的奇怪问题。(例如,您确定Map
没有使用Eq
和Ord
之间的关系吗?)
如果您根本不需要
Eq
实例,只需定义
instance Eq BigThing where
x == y = compare x y == EQ
那么,平等与比较是一致的。没有要求相等的值必须使所有字段相等
如果您需要一个比较所有字段的
Eq
实例,那么您可以通过将BigThing
包装成newtype
来保持一致,为其定义上述Eq
和Ord
,并在需要根据名称进行排序时在算法中使用它:
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
请注意,使用此解决方案,排序似乎是随机的,与字段没有明显的关系
您还可以将此解决方案与以前的解决方案相结合。或者,您可以创建一个小模块,用于包装任何类型及其预计算哈希(包装的值必须具有与其Hashable
实例一致的Eq
实例)
我已经这样做了,但是必须小心您使用的库。您是否需要根据名称
进行订购,或者是否可以接受任何类型的一致订购,只是为了使用映射
s并且速度快?任何订购都可以接受。如果您需要在所有字段上进行比较,我见过一两次的一种有趣的技术是计算一个散列并将其包含在数据结构中,然后对散列进行比较。
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