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

时间:2010-02-28 02:15:40

标签: sorting haskell tuples

我是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来做到这一点,但我自己无法弄明白。任何帮助将非常感谢!

7 个答案:

答案 0 :(得分:38)

您需要构建函数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")]

答案 1 :(得分:20)

我可以建议以下内容吗?

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出现(或EQ,如果所有元素都是EQ,那么两个对被认为是相等的。 / p>

第二个想法,没有必要建立清单。

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

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

mappend Ordering的定义基本上是:

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)

答案 2 :(得分:10)

首先我们应该制作排序函数,它需要两次操作并返回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")]

答案 3 :(得分:8)

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

重新FredOverflow's answer

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")]

答案 4 :(得分:1)

以下解决方案最适合我,一个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")]

答案 5 :(得分:0)

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

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需要两个参数的函数,所以函数组合不太好。

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

弗雷德指出,你可以写compare EQ代替invert。正如Dario指出的那样,我可以使用on中的Data.Function,但事实上on compare == comparing,我可以使用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 Master读取:

post

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

(我对这个答案没有任何投票,但是由于好的评论,我确实已经了解了很多关于Haskell库的内容。谁知道{{1}}等同于什么函数?不是Hoogle ... )

为对象编写合适的比较函数更为惯用,但您的问题要求连续排序。

答案 6 :(得分:0)

我喜欢Data.Ord中的比较功能。这基本上是格雷格以更紧凑的形式给出的答案:

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

[(2-,&#34;&#34),(2,&#34; B&#34),(1,&#34;&#34),(1,&#34; b&#34)]

&#34;比较fst&#34;根据元组的第一个元素给出一个排序。