【问题标题】:Summing all tuples with same keys (Implementing Haskell Data.Map.fromListWith) [duplicate]对具有相同键的所有元组求和(实现 Haskell Data.Map.fromListWith)[重复]
【发布时间】:2020-09-28 22:45:11
【问题描述】:

我想对具有相同键的元组的值求和。

例如,

foo = [(True, 0), (True, 1), (False, 2), (True, -1), (False,4)] :: [(Bool, Int)]
putStrLn . show $ sumTuples foo

应该显示 [(False,6),(True,0)].

当然,这很容易通过Data.Map.fromListWith 实现:

sumTuples :: (Ord a, Num b) => [(a, b)] -> [(a, b)]
sumTuples = Map.toList . Map.fromListWith (+)

但我想在没有Data.Map 的帮助下实现它。

使用过滤器,我可以这样解决:

sumByFilter :: [(Bool, Int)] -> [(Bool, Int)]
sumByFilter xs =
  let trues = filter((==True) . fst) xs
      falses = filter((==False) . fst) xs
      summing = sum . map snd
  in [(True, summing trues), (False, summing falses)]

或折叠:

sumByUglyFold :: [(Bool, Int)] -> [(Bool, Int)]
sumByUglyFold =
  let initial = [(True, 0), (False, 0)]
  in foldl foldingFxn initial

foldingFxn :: [(Bool,Int)] -> (Bool, Int) -> [(Bool, Int)]
foldingFxn [(_, truAcc), (_, falseAcc)] (bool, val)
  | bool      = [(True, truAcc + val), (False, falseAcc)]
  | otherwise = [(True, truAcc), (False, falseAcc + val)]

但是,在这两种情况下,我都将第一个参数硬编码为布尔值。但是,我希望能够对任何未预定义的键执行此操作——当然,如果我有字符串键,我无法对所有键进行模式匹配。

如何使我的代码通用?

【问题讨论】:

    标签: dictionary haskell tuples


    【解决方案1】:

    你必须按键排序,然后按它分组,然后对每个组求和:

    sumThem = map sumGroup . groupBy fstEq . sortOn fst
      where
        sumGroup (x:xs) = (fst x, sum $ map snd (x:xs))
        sumGroup _ = error "This can never happen - groupBy cannot return empty groups"
    
        fstEq (a, _) (b, _) = a == b
    

    请注意,此处排序是必不可少的操作,因为groupBy 仅对consequitive 元素进行分组。

    【讨论】:

    • 给未来读者的一些提示:fstEq 可以写成(==) `on` fst(与Data.Function.on)和sortOn f 对于琐碎的功能比sortBy (comparing f)(与Data.Ord.comparing)效率低像fst,因为它缓存了正在比较的键,但对于代价高昂的f更有效。
    • @JonPurdy 为了让答案简单明了,我特别不想提及所有这些。
    • 我理解并同意这是正确的做法,只是添加了一些补充信息。
    猜你喜欢
    • 2013-08-18
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-11-01
    • 1970-01-01
    相关资源
    最近更新 更多