Haskell - 按第一个第二个元素排序,然后按第一个元素排序

Haskell - Sort by first second element and then by first element

我有一个元组列表,我想按第二个元素(降序)排序,然后按第一个元素(升序)排序。

我的代码如下所示:

sortedOcc :: Eq a => [a] -> [(a, Int)]
sortedOcc = sortBy (flip compare `on` snd) . occurences

这是按 occurences(函数)返回的列表的第二个元素进行的第一次排序。我应该如何添加第一个元素的第二个排序(升序)?

sortBy 的签名是什么?

sortBy :: (a -> a -> Ordering) -> [a] -> [a]

这意味着它的第一个参数必须具有类型 a -> a -> Ordering:

sortedOcc :: Eq a => [a] -> [(a, Int)]
sortedOcc = sortBy g . occurences

g :: a -> a -> Ordering
g = (flip compare `on` snd)

但这意味着

g :: a -> a -> Ordering
g x y = (flip compare `on` snd) x y
      = flip compare (snd x) (snd y)
      = compare (snd y) (snd x)

所以要将您的要求添加到组合中,我们只需将其写下来,

      = let test1 = compare (snd y) (snd x)
            test2 = compare (snd y) (snd x)
        in ......

对吗?

以上故意包含错误,您应该很容易修复。

忠告,只使用point-free代码,如果您阅读和书写容易自然,并且修改

我可能会在 Ordering 和函数类型上使用 来写这个。

正如您已经确定的那样,按元组中的第二个值排序看起来像 flip compare `on` snd,而按第一个值排序看起来像 compare `on` fst。 这些可以与 <>.

单向组合
d :: [(String , Int)]
d = [("b", 1), ("a", 1), ("c",3), ("d",4)] 

sortedD = sortBy ((flip compare `on` snd) <> (compare `on` fst)) d

Data.Ord 模块提供了一个 Down 新类型,其目的只是为了颠倒顺序。

它还提供了一个comparing function:

comparing :: Ord a => (b -> a) -> b -> b -> Ordering

必须先输入一些转换函数,然后才能传递给sortBy

像这样:

$ ghci
 GHCi, version 8.8.4: https://www.haskell.org/ghc/  :? for help
 λ> 
 λ> sortBy  (comparing (\(a,v) -> (Down v, a)))   [(1,2),(1,3),(5,2),(5,3)]
 [(1,3),(5,3),(1,2),(5,2)]
 λ> 

转换函数返回的值然后使用它们自己的“自然”顺序进行排序。在我们的例子中,这是有序类型对上的 lexicographic order

总体而言,代码需要 Ord a 约束:

sortedOcc :: Ord a => [a] -> [(a, Int)]
sortedOcc = sortBy (comparing (\(a,v) -> (Down v, a)))  .  occurences

我知道其余的答案更短,但我建议您在使用已经 Haskell 实现的那些之前自己实现这些惰性函数,这样您就可以了解它是如何工作的。

-- Order a list of tuples by the first item
orderBy1stTupleItem :: Ord a => (a, b1) -> (a, b2) -> Ordering
orderBy1stTupleItem tup1 tup2
  | item1 > item2 = GT
  | item1 < item2 = LT
  | otherwise = EQ
  where
      item1 = fst tup1
      item2 = fst tup2

-- Order a list of tuples by the second item
orderBy2ndTupleItem :: Ord a1 => (a2, a1) -> (a3, a1) -> Ordering
orderBy2ndTupleItem tup1 tup2
  | item1 > item2 = GT
  | item1 < item2 = LT
  | otherwise = EQ
  where
      item1 = snd tup1
      item2 = snd tup2

-- Wrapper Function: Order a list of tuples by the first item and later by the second item
orderTuplesBy1stThenBy2ndItem :: (Ord a1, Ord a2) => [(a2, a1)] -> [(a2, a1)]
orderTuplesBy1stThenBy2ndItem listTuples = 
    sortBy orderBy2ndTupleItem (sortBy orderBy1stTupleItem listTuples)

例子

let exampleListTuples = [(1,2),(0,8),(6,1),(3,6),(9,1),(7,8),(0,9)]

然后让我们得到第一个列表,按每个元组的第一项排序:

> listOrderedByTuple1stItem = sortBy orderBy1stTupleItem exampleListTuples
> listOrderedByTuple1stItem
[(0,8),(0,9),(1,2),(3,6),(6,1),(7,8),(9,1)]

现在我们按每个元组的第二项对结果列表进行排序

> sortBy orderBy2ndTupleItem listOrderedByTuple1stItem
[(6,1),(9,1),(1,2),(3,6),(0,8),(7,8),(0,9)]

或者,您可以 运行 包装函数 orderTuplesBy1stThenBy2ndItem 如下:

> sortBy orderTuplesBy1stThenBy2ndItem exampleListTuples