Haskell 按fst过滤,按对的snd计数
我想定义函数pointCounts,它获取一个对列表,其中第一个成员是name,第二个是point值,并返回每个名称带有counted point的对列表 我为此挣扎了好几天,但我不知道该怎么做 输入示例应如下所示: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
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