这是内置的吗?

时间:2014-06-13 14:52:30

标签: list haskell tuples

我正在寻找类型

的功能

[[(a, b)]] -> [(a, [b])]

Hoogle tells me there isn't one,所以我写了这个:

transpose :: (Eq a) => [[(a, b)]] -> [(a, [b])]
transpose alists = uncurry zip $ foldl combine ([], []) alists
    where combine memo new = foldl tally memo new
          tally ([], []) (k, v) = ([k], [[v]])
          tally ((ksHead:ksRest), (vsHead:vsRest)) (k, v) = 
              if k == ksHead
              then (ksHead:ksRest, (v:vsHead):vsRest)
              else (ksHead:ks, vsHead:vs)
                  where (ks, vs) = tally (ksRest, vsRest) (k, v)

按重要性排序:

  1. Hoogle实际上是否存在内置的内容?
  2. transpose这个东西是正确的名字吗?
  3. 是否有更好的(更具可读性和/或更高性能)的写作方式?
  4. 修改

    因为有人对这种味道感兴趣:

    我正在编写一个堆栈排名应用,来自用户的选票以[(Candidate, Rank)]的形式出现。例如,为了计算获胜者Borda count,我需要通过组合这些选票来计算每个Candidate的排名。关于更普遍的问题的评论也是受欢迎的。

3 个答案:

答案 0 :(得分:6)

您应该考虑使用地图数据结构,这样可以更容易:

import Data.Map (Map)
import qualified Data.Map as Map

transpose: Ord a => [(k, v)] -> Map k [v]
transpose = Map.fromListWith (++) [] . map (\(k, v) -> (k, [v])

实际上,这基本上是文档为fromListWith提供的示例。

答案 1 :(得分:5)

transpose通常用于表示沿矩阵转置线的列表操作。事实上,Data.List.transpose就是这样做的。

我不完全确定您的代码正在做什么。说实话,这真是令人费解。你的意思是这样的吗?

import Data.List
import Data.Function
import Control.Arrow

groupByKey :: Ord a => [[(a, b)]] -> [(a, [b])]
groupByKey = map (fst . head &&& map snd) . groupBy kEq . sortBy kCmp . concat
  where
    kEq = (==) `on` fst
    kCmp = compare `on` fst

如果这是你正在做的事情,将约束升级到Ord a会将算法改进为O(n log n)而不是O(n ^ 2)。

答案 2 :(得分:1)

您可以通过使用单字母变量来帮助自己发现代码中的隐藏结构,而不是被您的名称选择隐含的含义分散注意力:

g :: (Eq a) => [[(a, b)]] -> [(a, [b])]
g alists = uncurry zip $ foldl (foldl f) ([], []) alists 
    where 
          f ([], []) (k, v) = ([k], [[v]])
          f ((h:t), (u:s)) (k, v)  
            | k == h    = (h:t, (v:u):s)
            | otherwise = (h:q,    u :r) where (q, r) = f (t, s) (k, v)
                        -- ((h:) *** (u:)) $ f (t,s) (k,v)

这有点不自然,处理unzip ped临时数据并zip将它们ping回输出。不需要,我们可以使用与输出相同类型的临时数据:

g2 :: (Eq a) => [[(a, b)]] -> [(a, [b])]
g2 alists = foldl (foldl f) [] alists 
    where 
          f [] (k, v) = [(k,[v])]
          f ((a,b):t) (k, v)  
            | k == a    = (a,v:b):t
            | otherwise = (a,b):f t (k,v)

现在很明显,f是一种"插入",paramorphism

          f xs (k, v) = para (\(a,b) t r -> if a==k then (a,v:b):t 
                                                    else (a,  b):r) 
                             [(k,[v])] xs
para c z []    = z
para c z (x:t) = c x t $ para c z t

foldl (foldl f) [] alists === foldl f [] $ concat alists,如果可以切换到(Ord a)约束,则可以通过重新实现f来提高效率,

g3 :: (Ord a) => [[(a, b)]] -> [(a, [b])]
g3 = foldl f [] . concat
  where
    f xs (k, v) = para (\(a,b) t r -> if k < a then (k,[v]):(a,b):t 
                                  else if a==k then (a,v:b):t
                                  else              (a,  b):r) 
                       [(k,[v])] xs

为了进一步提高此代码的复杂性,我们可以转到the other route(而不是concat)并通过合并树加入输入列表,

g4  :: (Ord a) => [[(a, b)]] -> [(a, [b])]
g4 alists = foldt u [] 
              . map (map (\(a,b) -> (a,[b])) . sortBy (comparing fst))
              $ alists
  where
    u xs [] = xs
    u [] ys = ys
    u xs@((a,b):t) ys@((c,d):r) = case compare a c of
        LT -> (a,b)    : u t ys
        EQ -> (a,b++d) : u t  r
        GT -> (c,d)    : u xs r

foldt f z []  = z
foldt f z [x] = x
foldt f z xs  = foldt f z $ pairwise f xs   -- tree-like folding
pairwise f (a:b:t) = f a b : pairwise f t
pairwise f xs      = xs

comparing来自Data.Ord。如果您的数据片段已经排序(可能在您的场景中),您可以省略sortBy部分以获得额外的算法增益。因此,这个版本是一种mergesort(可能只进行合并,没有排序)。