Haskell 按fst过滤,按对的snd计数

Haskell 按fst过滤,按对的snd计数,haskell,Haskell,我想定义函数pointCounts,它获取一个对列表,其中第一个成员是name,第二个是point值,并返回每个名称带有counted point的对列表 我为此挣扎了好几天,但我不知道该怎么做 输入示例应如下所示: pointCount[(“Ferp”,25),(“Herp”,18),(“Derp”,15),(“Ferp”,25),(“Herp”,15),(“Derp”,18),(“Jon”,10)] 以及所需的输出示例: [("Ferp",50),("Herp",33),("Derp",33

我想定义函数pointCounts,它获取一个对列表,其中第一个成员是name,第二个是point值,并返回每个名称带有counted point的对列表

我为此挣扎了好几天,但我不知道该怎么做

输入示例应如下所示:

pointCount[(“Ferp”,25),(“Herp”,18),(“Derp”,15),(“Ferp”,25),(“Herp”,15),(“Derp”,18),(“Jon”,10)]

以及所需的输出示例:

[("Ferp",50),("Herp",33),("Derp",33),("Jon",10)] 

我使用
Data.Map
作为中间数据结构:

import qualified Data.Map as M

pointCount :: Num a => [(String, a)] -> [(String, a)]
pointCount = M.toList . foldr f M.empty
    where f (name, val) = M.insertWith (+) name val
          -- or pointfree
          -- f = uncurry (M.insertWith (+))
或者更好(丹尼尔·瓦格纳指出)


我使用
Data.Map
作为中间数据结构:

import qualified Data.Map as M

pointCount :: Num a => [(String, a)] -> [(String, a)]
pointCount = M.toList . foldr f M.empty
    where f (name, val) = M.insertWith (+) name val
          -- or pointfree
          -- f = uncurry (M.insertWith (+))
或者更好(丹尼尔·瓦格纳指出)


下面是另一个解决方案,它只使用列表。但cdk的解决方案更简单,运行时间也更好

import Data.List

pointCount :: Num a => [(String, a)] -> [(String, a)]
pointCount [] = []
pointCount ((x, n):xs) =
    let (eqx, neqx) = partition ((==x).fst) xs in
        (x, n + (sum$ map snd eqx)) : pointCount neqx

下面是另一个解决方案,它只使用列表。但cdk的解决方案更简单,运行时间也更好

import Data.List

pointCount :: Num a => [(String, a)] -> [(String, a)]
pointCount [] = []
pointCount ((x, n):xs) =
    let (eqx, neqx) = partition ((==x).fst) xs in
        (x, n + (sum$ map snd eqx)) : pointCount neqx

假设结果的顺序无关紧要,并且只用于小列表(即效率并不重要),我会这样做:

下面是另一个可能的(类似的)解决方案,它利用了求和的半群性质,找到非空列表的第一项和半群对,以及在组合两个半群时得到一个半群(使用
半群
半群体
包):


我可能会选择第一种解决方案,但第二种方案说明了如何将问题的本质视为对半群组合的操作。该运算特别是一个半群同态,由
结果表示。foldMap1 packSemigroup
part.

假设结果的顺序无关紧要,并且只用于小列表(即效率并不重要),我会这样做:

下面是另一个可能的(类似的)解决方案,它利用了求和的半群性质,找到非空列表的第一项和半群对,以及在组合两个半群时得到一个半群(使用
半群
半群体
包):


我可能会选择第一种解决方案,但第二种方案说明了如何将问题的本质视为对半群组合的操作。该运算特别是一个半群同态,由
结果表示。foldMap1 packSemigroup
part.

如果名称可能很长,则可能需要对其进行哈希运算或使用trie或其他方法。@dfeuer(或者更确切地说,不知道“或”可能是什么的人)<代码>数据。HashMap存在,对于此类情况非常有用。存在两种实现,请参见
hashmap
unordered containers
包。我将把
critbit
包添加到列表中,它被设计成通过testring/
文本
键有效地索引
。性能与哈希映射大致相同,但它提供了哈希映射无法提供的一些便利函数(当然,这个答案不需要这些函数)。如果名称可能很长,则可能需要对其进行哈希或使用trie或其他方法。@dfeuer(或者更确切地说,不知道“或其他”可能是什么的人)<代码>数据。HashMap
存在,对于此类情况非常有用。存在两种实现,请参见
hashmap
unordered containers
包。我将把
critbit
包添加到列表中,它被设计成通过testring/
文本
键有效地索引
。性能与哈希映射大致相同,但它提供了哈希映射无法提供的一些便利函数(当然,这一答案不需要这些函数)。。我不认为这是一个重复的问题,但同样的问题正在解决。我不认为这是一个重复的问题,但同样的问题正在解决。
import           Data.Ord                  (comparing)
import           Data.Function             (on)
import           Data.List                 (groupBy, sortBy)
import           Data.Semigroup            (First (..), Sum (..))
import           Data.Semigroup.Foldable   (foldMap1)
import qualified Data.List.NonEmpty as NE
import           Control.Arrow             ((***))

pointCount :: Num a => [(String, a)] -> [(String, a)]
pointCount =   map     ( unpackResult
                       . foldMap1 packSemigroup
                       . NE.fromList
                       )
             . groupBy ((==) `on` fst)
             . sortBy  (comparing fst)
  where
    packSemigroup :: Num a => (String, a) -> (First String, Sum a)
    packSemigroup   = First *** Sum

    unpackResult  :: Num a => (First String, Sum a) -> (String, a)
    unpackResult = getFirst *** getSum