在Haskell中按特定属性对自定义数据类型列表进行排序

时间:2014-04-24 00:57:25

标签: sorting haskell

我们假设我们有自定义数据类型:

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 函数。这就是我被困的地方。任何人都可以帮助我吗?

编辑:是的,这是硬件 - 不幸的是我需要像我描述的那样去做。

2 个答案:

答案 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字段。由于IntOrd的实例,因此答案是即时的:

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 = ...

我会让您填写详细信息作为有用的练习。一旦完成,正常的排序功能将完成你想要的。