haskell - 压缩两个 HashMap
问题描述
假设我有两个 HashMap:
import Data.HashMap.Strict
m1 :: HashMap Char Int
m2 :: HashMap Char Int
??? :: (v1 -> v2 -> v3) -> HashMap k v1 -> HashMap k v2 -> HashMap k v3
-- or even
??? :: (k -> v1 -> v2 -> v3) -> HashMap k v1 -> HashMap k v2 -> HashMap k v3
我想要某种方式zip
走过这些地图,其中:
- 如果一个键存在于一个映射中但另一个映射中不存在,则将其替换为默认值(类似于“zip 最长”)
- 结果类型可能与原始类型不同。
intersectionWithKey
这与更接近 的语义的签名相匹配unionWithKey
。
一个具体的例子:对于一个(,)
使用 0 默认值的简单案例,我可以这样做
unionWith add (map (,0) m1) (map (0,) m2) :: HashMap Char (Int, Int)
add (a, x) (b, y) = (a+b, x+y)
这引发了一个问题:一般来说有没有一种干净的方法来做到这一点?
解决方案
一般来说:
- 合并两个映射以获取所有键的列表
- 定义一个新函数,给定一个键,用于
lookupDefault
从每个映射中获取适当的值(默认值或真实值),以便计算新映射的值 - 将该函数映射到键上,并根据结果创建一个新的哈希映射。
zipMap :: (v1 -> v2 -> v3) -- Combining function
-> v1 -- default value for keys unique to the second map
-> v2 -- default value for keys unique to the first map
-> HashMap k v1
-> HashMap k v2
-> HashMap k v3
zipMap f d1 d2 m1 m2 = let allKeys = keys (union m1 m2)
f' k = (k, f (lookupDefault d1 k m1) (lookupDefault d2 k m2))
in fromList $ map f' allKeys