Haskell:如果另一个属性相同,如何按一个属性对记录类型进行排序?

Haskell:如果另一个属性相同,如何按一个属性对记录类型进行排序?,haskell,record,newtype,Haskell,Record,Newtype,如果我有一个非常简单的自定义数据类型: data Person = Person {first_name :: String, last_name :: String } deriving (Ord, Eq, Show) 如果我有这些人员数据类型的列表,并且我想按姓氏对其排序,当且仅当姓氏相同时。但是我应该而不是对名字进行排序。简单地调用下面的代码显然不起作用 import Data.Function (on

如果我有一个非常简单的自定义数据类型:

data Person = Person {first_name :: String,
                      last_name :: String
                     } deriving (Ord, Eq, Show)
如果我有这些人员数据类型的列表,并且我想按
姓氏
对其排序,当且仅当
姓氏
相同时。但是我应该而不是
名字进行排序。简单地调用下面的代码显然不起作用

import Data.Function (on)
import Data.List (sortBy)

sortBy (compare `on` last_name ) persons
我怎样才能解决它?我同意这很令人困惑。我想要的是,如果我有以下内容(这肯定不是自定义数据类型的列表,我只想清楚地显示它)

分类后我想要

[("c","d"),("a","a"),("a","c")]
而不是

 [("a","a"),("a","c"),("c","d")]

因此,我们的想法是,第一个元素应该首先出现,因为它与其他两个元素没有相同的
first\u name
。可能吗?谢谢

您想要的比较函数是,如果两个
Person
值的名字不同,则将它们视为相等。如果名字相同,则根据姓氏进行比较

由于Haskell中的
sort
是稳定的,因此如果输入中的元素
a
先于相等的元素
b
,则输出中的元素也将先于
b
。在这种情况下,两个具有不同名字的元素将进行相等的比较,因此
sort
实际上只会对共享名字的连续子序列进行排序

可能有一种聪明的方法来实现这一点,但是一个简单的比较函数可能看起来像

foo :: Person -> Person -> Ordering
foo x y | first_name x /= first_name y = EQ
        | otherwise                   = (comparing last_name) x y
给定
people=[Person“A”B、“Person”Z“C”、Person“Z”A]
,您可以看到
sortBy(比较姓氏)
sortBy foo
之间的区别

> people
[Person {first_name = "A", last_name = "B"},Person {first_name = "Z", last_name = "C"},Person {first_name = "Z", last_name = "A"}]
> sortBy (comparing last_name) people
[Person {first_name = "Z", last_name = "A"},Person {first_name = "A", last_name = "B"},Person {first_name = "Z", last_name = "C"}]
> sortBy foo people
[Person {first_name = "A", last_name = "B"},Person {first_name = "Z", last_name = "A"},Person {first_name = "Z", last_name = "C"}]

您想要的比较函数是,如果两个
Person
值的名字不同,则将它们视为相等。如果名字相同,则根据姓氏进行比较

由于Haskell中的
sort
是稳定的,因此如果输入中的元素
a
先于相等的元素
b
,则输出中的元素也将先于
b
。在这种情况下,两个具有不同名字的元素将进行相等的比较,因此
sort
实际上只会对共享名字的连续子序列进行排序

可能有一种聪明的方法来实现这一点,但是一个简单的比较函数可能看起来像

foo :: Person -> Person -> Ordering
foo x y | first_name x /= first_name y = EQ
        | otherwise                   = (comparing last_name) x y
给定
people=[Person“A”B、“Person”Z“C”、Person“Z”A]
,您可以看到
sortBy(比较姓氏)
sortBy foo
之间的区别

> people
[Person {first_name = "A", last_name = "B"},Person {first_name = "Z", last_name = "C"},Person {first_name = "Z", last_name = "A"}]
> sortBy (comparing last_name) people
[Person {first_name = "Z", last_name = "A"},Person {first_name = "A", last_name = "B"},Person {first_name = "Z", last_name = "C"}]
> sortBy foo people
[Person {first_name = "A", last_name = "B"},Person {first_name = "Z", last_name = "A"},Person {first_name = "Z", last_name = "C"}]


如果这两个名称不相同,则不应对其进行排序。非常感谢。现在还不太清楚您计划如何只对一些元素进行排序,而不对其他元素进行排序。它不计算。您只能对整个列表进行排序。使用比较函数(将所有名字视为等效名称)的稳定排序应该可以做到这一点。@chepner这正是
compare`on`last\u name
所做的,OP声称它不起作用,也许不是因为默认排序不稳定。如果这两个排序没有相同的
名字怎么办?那么就不应该排序。非常感谢。现在还不太清楚您计划如何只对一些元素进行排序,而不对其他元素进行排序。它不计算。您只能对整个列表进行排序。使用将所有名字视为等效名称的比较函数进行稳定排序应该可以做到这一点。@chepner这正是
compare`on`last\u name
所做的,OP声称它不起作用,可能不是因为默认排序不稳定。非常感谢!我想这就是我想要的,虽然有点奇怪。但无论如何,这是一个奇怪的问题……我想你的意思是
名字x/=名字y
?我倾向于内联编写这个函数,除非它将被重用,例如,
sortBy(\ab->case comparing first\u name a b of{EQ->comparing last\u name a b;u->EQ})
或者在EQ a=a时,中断一个类似于
的辅助函数;whenEqual uuq=EQ
所以你可以写
whenEqual(比较姓a b)(比较姓a b)
;当相等比较名字和姓氏时,这也可以写为
liftA2,虽然这可能有点太远了,但是hahaThis的比较并没有强加一个非严格的偏序。关系约翰·布莱克相当于玛丽亚·埃尔南德斯,玛丽亚·埃尔南德斯相当于约翰·布朗,但约翰·布朗并不等同于约翰·布莱克。文档中没有说明使用不遵守非严格偏序法则的比较函数时,
sortBy
应该做什么,我很难想象使用这种比较进行排序可能意味着什么。更正,Haskell 2010年的报告说“假定该函数定义了总排序”,所以它是未定义的
sortBy foo
将做什么。如果
sortBy
在给定总排序的情况下保证是稳定的,那么这是否意味着对
foo
进行了什么调用?如果
fooab==EQ
,我们只能暗示
a非常感谢!我想这就是我想要的,虽然有点奇怪。但无论如何,这是一个奇怪的问题……我想你的意思是
名字x/=名字y
?我倾向于内联编写这个函数,除非它将被重用,例如,
sortBy(\ab->case comparing first\u name a b of{EQ->comparing last\u name a b;u->EQ})
或者在EQ a=a时,中断一个类似于
的辅助函数;whenEqual uuq=EQ
所以你可以写
whenEqual(比较姓a b)(比较姓a b)
;当相等比较名字和姓氏时,这也可以写为
liftA2,虽然这可能有点太远了,但是hahaThis的比较并没有强加一个非严格的偏序。关系约翰·布莱克相当于玛丽亚·埃尔南德斯,玛丽亚·埃尔南德斯相当于约翰·布朗,但约翰·布朗并不等同于约翰·布莱克。文献