在不使用sortBy的情况下,按第二个元素对元组列表进行排序

时间:2016-05-08 09:41:09

标签: list haskell tuples

假设函数的输入是:

[(6,-275),(3,123),(7,1000),(9,-572),(3,333),(1,-23)]

输出应为:

[(9,-572),(6,-275),(1,-23),(3,123),(3,333),(7,1000)]

这是我想要的功能:

sortBySnd :: Ord a => [(a,a)] -> [(a,a)]
sortBySnd [] = []
sortBySnd [(_,a),(_,b)]
    | a < b = [(_,a),(_,b)]
    | a > b = [(_,b),(_,a)]
    | a == b = [(_,a),(_,b)]

这当然是非常错误的,但我只想展示我想要实现的目标。

对于排序函数,可以使用mergesort或除sortBy之外的一些内置排序函数。

3 个答案:

答案 0 :(得分:5)

这就是我的意思:

import Data.List (sort)
import Data.Tuple (swap)

sortT = map swap . sort . map swap

答案 1 :(得分:2)

只是为了好玩,使用Data.Map

uncurry (fmap.flip(,))  -- distribute the keys over the values
<=< toList              -- convert back to a list, now sorted
. fromListWith (++)     -- convert to Map, collect values with same keys
. fmap (fmap(:[]).swap) -- swap the pairs and promote snd to a list

答案 2 :(得分:1)

正如Carsten所做的那样,你可以通过“屏蔽”第一个元素然后只使用正常排序来实现这一点。翻转元素实现了这一点,虽然它相当糟糕(第一个元素仍然会被排序,只有较低的优先级)...一个更好的方法是使用一个真正比较的类型第二个要素:

data SndOrd a b = SndOrd a b

instance Eq SndOrd where SndOrd _ x == SndOrd _ y = x==y
instance Ord SndOrd where compare (SndOrd _ x) (SndOrd _ y) = compare x y

然后你可以做

sortT = map (\(SndOrd a b) -> (a,b)) . sort . map (\(a,b) -> SndOrd a b)

但是,我不建议这样做,因为通常期望==是恰当的平等,但是使用这种方法你有SndOrd 1 2 == SndOrd 3 2

当然,正确的方法是使用 sortBy!或者自己实施。编写一般的排序算法是一个可以在互联网上找到资源的主题,你应该在询问StackOverflow之前先查看一下...

你自己的尝试并非毫无意义。首先,我们需要通过递归来实际覆盖除0或2以外的长度列表:

sortBySnd' ((_,a):(_,b):l)
    | a < b  = (_,a) : sortBySnd' ((_,b):l)
    | a > b  = (_,b) : sortBySnd' ((_,a):l)
    | a == b = (_,a) : sortBySnd' ((_,b):l)
sortBySnd' l = l  -- lists with length <2 are already sorted!

请注意,您可以统一第一和第三名警卫:

sortBySnd' ((_,a):(_,b):l)
    | a > b     = (_,b) : sortBySnd' ((_,a):l)
    | otherwise = (_,a) : sortBySnd' ((_,b):l)
sortBySnd' l = l  -- lists with length <2 are already sorted!

现在,这还不行,因为每个元素最多可以在列表中向后移动一个单元格。你可以通过迭代整个过程来挽救这个,就像列表中有元素一样:

sortBySnd'' l = iterate sortBySnd' l !! length l

这是bubble sort,这是一种非常低效的算法,但它可以工作。