我是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
来做到这一点,但我自己无法弄明白。任何帮助将非常感谢!
答案 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)
首先我们应该制作排序函数,它需要两次操作并返回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")]
答案 3 :(得分:8)
恭喜您迈出了学习Haskell的第一步。这是一段美好的旅程!
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;根据元组的第一个元素给出一个排序。