如何在Haskell中为代数数据类型实现“Ord”?

9

想象一下你有一个像这样的评分

Rating = OneStar | TwoStars | ThreeStars | FourStars | FiveStars

什么是在Haskell中实例化/实现“Ord”用于这种代数数据类型的最佳方法?
3个回答

18
最好的方式是只需在类型定义中添加deriving (Eq, Ord)。由于您按升序列出了构造函数,派生的Ord实例将给出您想要的确切顺序。但是,如果定义的顺序与所需的比较顺序不匹配且出于某种原因无法更改定义的顺序,则仍然可以派生Eq,因为对于这个不需要考虑顺序。给定Eq的实例,我们可以手动编写一个Ord的实例。定义compare最简洁的方式可能是拼写应返回LT的所有组合,然后对于剩余的组合,只需使用compare x y | x == y = Eq; compare _ _ = GT即可。

谢谢,我不知道顺序是被保留的。 - LennyStackOverflow
奖励问题:如果定义的顺序不反映数字顺序,会怎么样? - LennyStackOverflow
2
@Lenny:那么你需要实例化Ord并手动定义compare(假设由于某些原因更改定义中的顺序不是选项)- 但您仍然可以派生Eq,因为对于它来说顺序并不重要。最简洁的定义compare的方法可能是拼写出所有应返回LT的组合,然后对于其余组合只需使用compare x y | x == y = Eq; compare _ _ = GT即可。 - sepp2k
3
更简单的解决方案:改变定义,使其按升序排列! - Dan Burton
1
@Lenny,为了一些协议工作,我甚至添加了未使用的占位符:data x = Val0 | Val1 | Val2 | Unused3 | Unused4 | Val5。有时这是最快,最简单的方法,但如果有很大的间隔,显然不是最好的选择。 - Thomas M. DuBuisson

3

正如已经提到的,您可以推导出EqOrd。或者您可以推导出Enum,然后执行

instance Eq Rating where
    x == y = fromEnum x == fromEnum y

或者完全拼写出来。
instance Eq Rating where
    OneStar == OneStar = True
    TwoStar == TwoStar = True
...
    _ == _ = False

2

另一种实现compare的方法是回退到已经是Ord实例的类型。

data Rating = OneStar | TwoStars | ThreeStars | FourStars | FiveStars
    deriving (Show, Eq)

instance Ord Rating where
  compare x y = compare (r2n x) (r2n y)
    where
      r2n OneStar = 1
      r2n TwoStars = 2
      r2n ThreeStars = 3
      r2n FourStars = 4
      r2n FiveStars = 5

我认为这个解决方案的可扩展性相当不错。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接