我想定义一个函数pointCounts,它取一个第一个成员是名字的对的列表,然后是每个名字的计数点对的点值和返回列表。
我为此奋斗了好几天,但我无法想象如何做到这一点。
输入示例应如下所示:
pointCount [("Ferp",25),("Herp",18),("Derp",15),("Ferp",25),("Herp",15),("Derp",18),("Jon",10)]
所需的输出示例:
[("Ferp",50),("Herp",33),("Derp",33),("Jon",10)]
答案 0 :(得分:6)
我使用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 (+))
甚至更好(正如Daniel Wagner指出的那样)
pointCount = M.toList . M.fromListWith (+)
答案 1 :(得分:1)
这是另一种使用比列表更具异国情调的解决方案。但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
答案 2 :(得分:1)
这是我怎么做的,假设结果的顺序并不重要,而且只用在小清单上(即效率并不重要) :
import Data.Ord (comparing)
import Data.Function (on)
import Data.List (groupBy, sortBy, foldl1')
pointCount :: Num a => [(String, a)] -> [(String, a)]
pointCount = map (foldl1' sumSecond)
. groupBy ((==) `on` fst)
. sortBy (comparing fst)
where
sumSecond :: Num a => (String, a) -> (String, a) -> (String, a)
sumSecond (_, accum) (name, v) = (name, accum + v)
这是另一个可能的(类似的)解决方案,利用求和的半群性质,找到非空列表的第一项和半群对,以及得到半群的事实当你组成两个半群时(使用semigroups
和semigroupoids
包):
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
我可能会选择第一个解决方案,但第二个解决方案说明了如何将问题的本质视为对半群组合的操作。该操作具体是半群同态,由unpackResult . foldMap1 packSemigroup
部分表示。