如何按Erlang映射列表的键对各对应值进行汇总?
解决Erlang映射列表的汇总问题
嘿,我完全理解你碰到的困扰——Erlang的不可变变量特性确实会让这种累加操作和你熟悉的命令式写法不一样,不过咱们可以用函数式编程里的折叠(fold)操作来完美解决这个问题,这是处理这类累加场景的标准方案!
为什么你的原代码行不通?
你提到的列表推导写法的问题在于:Erlang里变量一旦绑定就不能修改,所以每次迭代你都是基于最初的Summary(#{votes=>0, likes=>0, views=>0})去生成新的映射,而不是用上一次迭代得到的更新后的Summary继续累加。最后你得到的会是一个包含三个独立映射的列表,而不是一个汇总后的结果。
正确的实现方式:用lists:foldl/3累加
lists:foldl会帮你维护一个累加器(accumulator),每次迭代都用当前元素去更新这个累加器,最终返回汇总后的结果。这里有两种写法:
写法1:直接匹配键值(直观易懂)
% 你的原始映射列表 Map = [#{votes=>3, likes=>20, views=> 100},#{votes=>0, likes=>1, views=> 70},#{votes=>1, likes=>14, views=> 2000}]. % 用foldl进行累加 Summary = lists:foldl(fun(MapItem, Acc) -> % 从累加器和当前映射中取出对应的值 #{votes := AccVotes} = Acc, #{votes := ItemVotes} = MapItem, #{likes := AccLikes} = Acc, #{likes := ItemLikes} = MapItem, #{views := AccViews} = Acc, #{views := ItemViews} = MapItem, % 生成新的累加器(Erlang里是创建新映射,不是修改旧的) #{votes => AccVotes + ItemVotes, likes => AccLikes + ItemLikes, views => AccViews + ItemViews} end, % 初始累加器:所有键的初始值为0 #{votes => 0, likes => 0, views => 0}, % 要处理的映射列表 Map).
运行这段代码后,Summary的值会是:#{votes => 4, likes => 35, views => 2170},完全符合你的需求。
写法2:通用的映射合并函数(更简洁)
如果你的映射以后可能增加更多键,或者想让代码更通用,可以写一个专门合并两个映射的函数,再结合foldl使用:
% 定义一个合并两个映射的函数:将第二个映射的值累加到第一个上 sum_maps(AccMap, ItemMap) -> maps:fold(fun(Key, ItemVal, UpdatedAcc) -> % 用update_with累加值:如果键存在就相加,不存在则设置为ItemVal(这里初始Acc已经有所有键,所以不会触发后者) maps:update_with(Key, fun(AccVal) -> AccVal + ItemVal end, ItemVal, UpdatedAcc) end, AccMap, ItemMap). % 用foldl调用这个函数 Summary = lists:foldl(fun sum_maps/2, #{votes => 0, likes => 0, views => 0}, Map).
这个写法的好处是,哪怕以后你的映射新增了其他数值类型的键,不需要修改折叠的逻辑,只需要确保初始累加器包含对应的键即可。
内容的提问来源于stack exchange,提问作者Jon Riel
相关产品推荐
相关产品推荐

