我们假设我们有自定义数据类型:
data Person = Person { first_name :: String,
last_name :: String,
age :: Int
} deriving (Ord, Eq, Show)
我们还说我有这些Person数据类型的列表。我已经创建了一个按顺序对这些人进行排序的功能,但这仅限于每个人的第一个值first_name。我尝试做的是修改Person数据类型,以便此sort函数按年龄而不是first_name排序(除了交换值顺序以便年龄是第一个)。 我知道我需要使用 instance 关键字为Ord编写自己的 compare 函数。这就是我被困的地方。任何人都可以帮助我吗?
编辑:是的,这是硬件 - 不幸的是我需要像我描述的那样去做。
答案 0 :(得分:17)
处理该问题的最简单,最灵活的方法,假设有多种有效的方法来排序Person
值,不是通过更改Ord
实例,而是通过使用自定义排序功能
import Data.List (sortBy)
import Data.Ord (comparing)
sortByAge :: [Person] -> [Person]
sortByAge = sortBy (comparing age)
sortBy :: (a -> a -> Ordering) -> [a] -> [a]
从比较函数生成自定义排序函数,而comparing :: Ord a => (b -> a) -> b -> b -> Ordering
生成这样的比较函数,例如,字段acessor。一定要仔细查看所涉及的类型签名,以便了解所有内容是如何组合在一起的。
如果您确实需要更改Ord
实例,请按以下步骤操作。语法如下:
instance Ord Person where
compare = undefined -- placeholder
The documentation告诉我们,在所有Ord
方法中,我们只需要实现compare
。现在,我们应该用undefined
取代什么?我们希望比较基于age
,即Int
字段。由于Int
是Ord
的实例,因此答案是即时的:
instance Ord Person where
compare x y = compare (age x) (age y)
顺便说一下,definition of comparing
是:
comparing :: (Ord a) => (b -> a) -> b -> b -> Ordering
comparing p x y = compare (p x) (p y)
因此我们可以用我们用于第一个解决方案的样式编写实例:
instance Ord Person where
compare = comparing age
答案 1 :(得分:2)
从派生类型类列表中删除Ord
,如果必须编写自己的实例,则编译生成的实例将发生冲突。此外,这不是惯用的,我不推荐这一点。
您现在可以通常的方式自己编写Ord实例:
instance Ord Person where
compare b1 b2 = ...
我会让您填写详细信息作为有用的练习。一旦完成,正常的排序功能将完成你想要的。