【问题标题】:Sorting a list of tuples by second element without using sortBy在不使用 sortBy 的情况下按第二个元素对元组列表进行排序
【发布时间】:2016-09-03 01:30:17
【问题描述】:

假设函数的输入是:

[(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以外的一些内置排序函数。

【问题讨论】:

  • 如果你想变得棘手,你可以:1. 翻转元组,2. 使用排序,3. 再次翻转

标签: list haskell tuples


【解决方案1】:

这就是我的意思:

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

sortT = map swap . sort . map swap

【讨论】:

  • 或:sortT = map swap . sort . map swap
  • 是的:两者都是真的 ;) - 顺便说一句:我有点认为它是一个 功能,如果第二个结果相同,它将对第一个进行排序 ;)
【解决方案2】:

正如 Carsten 所做的那样,您可以通过“屏蔽”第一个元素然后使用普通排序来实现这一点。翻转元素可以实现这一点,尽管它是一种 hack(第一个元素仍然会被排序,只是优先级较低)......更好的方法是使用真正比较的类型 only 第二个元素:

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!或者自己实现。编写通用排序算法是您可以在 Internet 上找到 大量 资源的主题,您应该先查看这些资源,然后再在 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,一个非常低效的算法,但它有效。

【讨论】:

    【解决方案3】:

    只是为了好玩,使用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
    

    【讨论】:

    • 顺便说一句,(uncurry (fmap . flip (,)) &lt;=&lt;) 这里与(concatMap (\(k,as)-&gt; (, k) &lt;$&gt; as) .) 相同。 --- 另外,如果排序需要稳定,应该使用fromListWith (flip (++))
    猜你喜欢
    • 2012-03-08
    • 2012-03-13
    • 2022-08-18
    • 1970-01-01
    • 1970-01-01
    • 2011-05-21
    • 1970-01-01
    • 2016-03-17
    • 2015-06-16
    相关资源
    最近更新 更多