Haskell映射上的和

Haskell映射上的和,haskell,map,Haskell,Map,是否有标准函数对Haskell映射中的所有值求和。我的地图读起来像[(a,2),(b,4),(c,6)] 基本上我想做的是一个标准化的频率分布。因此,上述映射中键的值是a、b、c的计数。我需要将它们规范化为[(a,1/6),(b,1/3),(c,1/2)]您只需执行Map.foldl'(+)0(或者M.foldl',如果您导入数据。Map asM) 这就像foldl'(+)0。Map.elems,但效率略高。(不要忘记撇号-使用foldl或foldr对标准数字类型(Int、Integer、Flo

是否有标准函数对Haskell映射中的所有值求和。我的地图读起来像[(a,2),(b,4),(c,6)]


基本上我想做的是一个标准化的频率分布。因此,上述映射中键的值是a、b、c的计数。我需要将它们规范化为[(a,1/6),(b,1/3),(c,1/2)]

您只需执行
Map.foldl'(+)0
(或者
M.foldl'
,如果您导入数据。Map as
M

这就像
foldl'(+)0。Map.elems
,但效率略高。(不要忘记撇号-使用foldl或foldr对标准数字类型(Int、Integer、Float、Double等)进行求和将产生巨大的thunk,这将占用大量内存,并可能导致程序溢出堆栈。)

但是,只有足够新的(>=0.4.2.0)版本才包含,您不应该使用
cabal install
对其进行升级,因为它附带GHC。因此,除非您使用的是GHC7.2或更高版本,
foldl'(+)0。Map.elems
是实现这一点的最佳方法

您也可以使用,它可以在typeclass的任何实例上工作,但仍然会在常见的数字类型上生成大量Thunk

下面是一个完整的示例:

normalize :: (Fractional a) => Map k a -> Map k a
normalize m = Map.map (/ total) m
  where total = foldl' (+) 0 $ Map.elems m
您需要导入数据。使用
foldl'
的列表

let
    total = foldr (\(_, n) r -> r + n) 0 l
in map (\(x, y) -> (x, y/total) l
其中
l
是您的地图。

简单:

import qualified Data.Map as M

sumMap = M.foldl' (+) 0

normalizeMap m =
  let s = sumMap m in
    M.map (/ s) m

main = do
  let m = M.fromList [("foo", 1), ("bar", 2), ("baz", 6)]
  (print . sumMap) m
  (print . normalizeMap) m
印刷品:

9.0
fromList [("bar",0.2222222222222222),("baz",0.6666666666666666),("foo",0.1111111111111111)]

好问题。显而易见的
foldl
解决方案对于在树上求和是非常不规范的
Data.Foldable.sum
将分别对每个分支求和,然后合并结果,但它不是并行的或任何东西,因此这样做没有真正的好处(它有我在回答中提到的严格性问题)。并行解决方案可能很有趣,但可能只会获得足够大的映射(此时您可能应该使用来自或类似的HashMap;Data.Map不是一个特别有效的结构)。呃…我的是一个非常庞大的数据集。事实上,我决定不使用哈希表,因为我在Haskell中读到了结构的性能问题。你提到的HashMap结构在使用上类似吗?嗯,标准的library Data.HashMap有点差劲,但是无序容器的数据。HashMap与Data.Map有着几乎相同的API(虽然函数更少),但速度更快;它需要一个哈希实例,而不是Ord实例,但这只是
a->Int
的问题。如果您确实需要不惜一切代价获得最大性能,那么这可能是一个不错的选择,但它只是可变的。如果可能的话,我会选择HashMap。这会给我一个“不在范围内:Map.foldl”错误吗?我的导入似乎还可以。@atlantis,那是因为您使用的是旧版本的容器库。