[英]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
Ordering
的mappend
定义基本上是:
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)
首先我们应该使订单函数接受两次操作并返回EQ
, LT
或GT
(即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.