简体   繁体   English

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

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

I am a beginner in Haskell so please bear with me. 我是Haskell的初学者所以请耐心等待。 (Just started learning yesterday!) How can I sort a list of tuples primarily by their first components (highest to smallest) and secondarily by their second components (smallest to highest)? (刚开始学习昨天!)我如何主要按照第一个组件(从最高到最小)排序元组列表,然后按第二个组件(从最小到最高)排序? A sample input/output would be: 输入/输出的示例如下:

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

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

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

I tried using the following but it gave wrong output: 我尝试使用以下但它输出错误:

sortGT a b = GT

sortBy sortGT lst

I am sure that I can do this by using sortBy only, but I can't figure it out myself. 我确信我只能通过使用sortBy来做到这一点,但我无法弄清楚自己。 Any help would be highly appreciated! 任何帮助将非常感谢!

You need to construct your function sortGT , so that it compares pairs the way you want it: 您需要构造函数sortGT ,以便按照您希望的方式比较对:

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


Using this you get the following results (I used ghci): 使用这个你得到以下结果(我使用ghci):

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

May I suggest the following? 我可以建议如下吗?

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

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

You can then sort by writing sortBy myPredicate lst . 然后,您可以通过编写sortBy myPredicate lst来进行排序。 The function mconcat simply scans through the list and obtains the first non- EQ occurence (or EQ if all elements are EQ and thus both pairs are considered equal). 功能mconcat简单地通过列表扫描并获得第一非EQ occurence(或EQ如果所有元素都是EQ并因此既对被认为是相等的)。

On second thought, building the list isn't necessary. 再想一想,建立清单是没有必要的。

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

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

The definition of mappend for Ordering is essentially: Orderingmappend定义基本上是:

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

Which is exactly what we need. 这正是我们所需要的。

Just for fun, generalizing gbacon's answer and making the use a little more flexible: 只是为了好玩,概括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)

First we should make the ordering function wich takes two touples and returns either EQ , LT or GT (ie. sortGT :: (a,b) -> (a,b) -> Ordering .) Then we can give this ordering function to sortBy and it will sort it's input according to this ordering. 首先我们应该使订单函数接受两次操作并返回EQLTGT (即sortGT :: (a,b) -> (a,b) -> Ordering 。)然后我们可以给这个排序函数sortBy ,它将根据此顺序对其输入进行排序。

Since you want the first components to have first priority, we check that first and if they are equal we check the second argument,if the first components is not equal we give it the opposite value of it's original ordering, so that it is ordered highest to lowest. 由于你希望第一个组件具有第一优先级,我们首先检查它们,如果它们相等,我们检查第二个参数,如果第一个组件不相等,我们给它与原始排序的相反值,以便它被排序最高到最低点。

This is what I think is easiest on the eyes : 这是我认为最容易看到的:

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

Now we use sortBy as you suggested : 现在我们按照您的建议使用sortBy:

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

Congratulations on taking your first steps to learn Haskell. 恭喜您迈出了学习Haskell的第一步。 It's a great journey! 这是一段美好的旅程!

Riffing on FredOverflow's answer : 重复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

Output: 输出:

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

The following solution works best for me, a Haskell newbie. 以下解决方案最适合我,一个Haskell新手。 It looks a lot like 3lectrologos ' answer: I actually only added the function definition and the List import, but it can cause some confusion if left out. 它看起来很像3lectrologos的答案:我实际上只添加了函数定义和List导入,但是如果省略它会引起一些混淆。

Create a function 'myCompare' and don't forget to import the List module. 创建一个'myCompare'函数,不要忘记导入List模块。 You'll need it to make the sortBy work. 你需要它来使sortBy工作。 The function should look like this: 该函数应如下所示:

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

After loading the Haskell file, you can write the following in your terminal: 加载Haskell文件后,您可以在终端中编写以下内容:

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

Which will return: 哪个将返回:

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

It's always tricky composing two-argument functions. 组成双参数函数总是很棘手。 Here's an implementation: 这是一个实现:

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')

Because sortBy expects a function of two arguments, function composition isn't so nice. 因为sortBy需要两个参数的函数,所以函数组合并不是那么好。

I have tested this code, and it works on your example. 我已经测试了这段代码,它适用于您的示例。

As Fred points out, you can write compare EQ instead of invert . 正如Fred指出的那样,你可以写出compare EQ而不是invert As Dario points out, I could be using on from Data.Function , but in fact on compare == comparing , which I can use instead. 正如Dario指出的那样,我可以on Data.Function上使用,但事实上on compare == comparing ,我可以使用它。 Now the code can be read only by a Haskell Master: 现在代码只能由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')

I have compiled and run this code and it works on the original example. 我已编译并运行此代码,它适用于原始示例。

(I haven't got any votes for this answer, but thanks to good comments, I sure have learned a lot about the Haskell library. Who knows what function post is equivalent to? Not Hoogle...) (我对这个答案没有任何投票,但是由于好的评论,我确实已经了解了很多关于Haskell库的内容。谁知道什么功能post相当于?不是Hoogle ...)

It would be more idiomatic to write a suitable comparison function for pairs , but your question asked for consecutive sorts. 为对编写一个合适的比较函数更加惯用 ,但是你的问题要求连续排序。

I like the comparing function in Data.Ord. 我喜欢Data.Ord中的比较函数。 This is basically Greg's answer in even more compact form: 这基本上是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")] [(2, “A”),(2, “B”),(1, “A”),(1, “B”)]

"comparing fst" gives an ordering based on the first element of the tuple. “比较fst”给出了基于元组的第一个元素的排序。

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

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