繁体   English   中英

在Haskell中,如何使用内置的sortBy函数对对象列表(元组)进行排序?

[英]In Haskell, how can I use the built in sortBy function to sort a list of pairs(tuple)?

我是Haskell的初学者所以请耐心等待。 (刚开始学习昨天!)我如何主要按照第一个组件(从最高到最小)排序元组列表,然后按第二个组件(从最小到最高)排序? 输入/输出的示例如下:

[(1, "b"), (1, "a"), (2, "b"), (2, "a")] (输入)

[(1, "a"), (2, "a"), (1, "b"), (2, "b")] (中间步骤)

[(2, "a"), (2, "b"), (1, "a"), (1, "b")] (输出)

我尝试使用以下但它输出错误:

sortGT a b = GT

sortBy sortGT lst

我确信我只能通过使用sortBy来做到这一点,但我无法弄清楚自己。 任何帮助将非常感谢!

您需要构造函数sortGT ,以便按照您希望的方式比较对:

sortGT (a1, b1) (a2, b2)
  | a1 < a2 = GT
  | a1 > a2 = LT
  | a1 == a2 = compare b1 b2


使用这个你得到以下结果(我使用ghci):

*Main Data.List> sortBy sortGT [(1, "b"), (1, "a"), (2, "b"), (2, "a")]
[(2,"a"),(2,"b"),(1,"a"),(1,"b")]

我可以建议如下吗?

import Data.List (sortBy)
import Data.Monoid (mconcat)

myPredicate (a1, a2) (b1, b2) = mconcat [compare b1 a1, compare a2 b2]

然后,您可以通过编写sortBy myPredicate lst来进行排序。 功能mconcat简单地通过列表扫描并获得第一非EQ occurence(或EQ如果所有元素都是EQ并因此既对被认为是相等的)。

再想一想,建立清单是没有必要的。

import Data.List (sortBy)
import Data.Monoid (mappend)

myPredicate (a1, a2) (b1, b2) = compare b1 a1 `mappend` compare a2 b2

Orderingmappend定义基本上是:

EQ `mappend` x = x
x  `mappend` _ = x

这正是我们所需要的。

只是为了好玩,概括gbacon的答案并使用更灵活:

import Data.Ord
import Data.List
import Data.Monoid

ascending  = id
descending = flip

sortPairs f x g y = f (comparing x) `mappend` g (comparing y)

mySort = sortBy (sortPairs descending fst ascending snd)

首先我们应该使订单函数接受两次操作并返回EQLTGT (即sortGT :: (a,b) -> (a,b) -> Ordering 。)然后我们可以给这个排序函数sortBy ,它将根据此顺序对其输入进行排序。

由于你希望第一个组件具有第一优先级,我们首先检查它们,如果它们相等,我们检查第二个参数,如果第一个组件不相等,我们给它与原始排序的相反值,以便它被排序最高到最低点。

这是我认为最容易看到的:

sortGT (a1,b1) (a2,b2) = 
  case compare a1 a2 of
    EQ -> compare b1 b2
    LT -> GT
    GT -> LT

现在我们按照您的建议使用sortBy:

*Main> sortBy sortGT [(1, "b"), (1, "a"), (2, "b"), (2, "a")]
[(2,"a"),(2,"b"),(1,"a"),(1,"b")]

恭喜您迈出了学习Haskell的第一步。 这是一段美好的旅程!

重复FredOverflow的回答

import Data.Ord
import Data.List
import Data.Monoid

main :: IO ()
main = do
  print $ sortBy cmp [(1, "b"), (1, "a"), (2, "b"), (2, "a")]
  where
    cmp = flip (comparing fst) `mappend` comparing snd

输出:

[(2,"a"),(2,"b"),(1,"a"),(1,"b")]

以下解决方案最适合我,一个Haskell新手。 它看起来很像3lectrologos的答案:我实际上只添加了函数定义和List导入,但是如果省略它会引起一些混淆。

创建一个'myCompare'函数,不要忘记导入List模块。 你需要它来使sortBy工作。 该函数应如下所示:

import Data.List

myCompare :: (Ord a, Ord b) => (a,b) -> (a,b) -> Ordering  
myCompare (a1,b1) (a2,b2)
     | a1 < a2     = GT  
     | a2 == a1    = EQ  
     | otherwise = LT

加载Haskell文件后,您可以在终端中编写以下内容:

*Main> sortBy myCompare [(1, "b"), (1, "a"), (2, "b"), (2, "a")]

哪个将返回:

[(2,"a"),(2,"b"),(1,"a"),(1,"b")]

组成双参数函数总是很棘手。 这是一个实现:

invert :: Ordering -> Ordering
invert GT = LT
invert LT = GT
invert EQ = EQ


sosort :: (Ord a, Ord b) => [(a, b)] -> [(a, b)]
sosort = sortBy (\p p' -> invert $ uncurry compare $ double fst p p') . 
         sortBy (\p p' ->          uncurry compare $ double snd p p')
  where double f a a' = (f a, f a')

因为sortBy需要两个参数的函数,所以函数组合并不是那么好。

我已经测试了这段代码,它适用于您的示例。

正如Fred指出的那样,你可以写出compare EQ而不是invert 正如Dario指出的那样,我可以on Data.Function上使用,但事实上on compare == comparing ,我可以使用它。 现在代码只能由Haskell Master读取:

sosort :: (Ord a, Ord b) => [(a, b)] -> [(a, b)]
sosort = sortBy (compare EQ `post` comparing fst) . sortBy (comparing snd)
  where post f g x x' = f (g x x')

我已编译并运行此代码,它适用于原始示例。

(我对这个答案没有任何投票,但是由于好的评论,我确实已经了解了很多关于Haskell库的内容。谁知道什么功能post相当于?不是Hoogle ...)

为对编写一个合适的比较函数更加惯用 ,但是你的问题要求连续排序。

我喜欢Data.Ord中的比较函数。 这基本上是Greg在更紧凑的形式中的答案:

Prelude Data.Ord Data.List Data.Function> (reverse.sortBy (comparing fst)) [(1, "b"), (1, "a"), (2, "b"), (2, "a")]

[(2, “A”),(2, “B”),(1, “A”),(1, “B”)]

“比较fst”给出了基于元组的第一个元素的排序。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM