我需要创建
def MergeWith[Id, X, Y, Z](x: HashMap[Id, X], y: HashMap[Id, Y], f: (X, Y) => Z): HashMap[Id, Z]
我想:
1)迭代x
2)对于y中显示的键,f(x[key], y[key])
生成HashMap[Id, Z]
对于我发明的一切,我得到编译错误,我不明白如何战斗。
例如,
def andWith[K, X, Y, Z] (x: HashMap[K, X], y: HashMap[K, Y], f: (X, Y) => Z): HashMap[K, Z] = {
for {
(x_name, x_value) <- x
if y.contains(x_name)
} yield x_name -> f(x_value, y.get(x_name))
}
产生
Error:(14, 39) type mismatch;
found : Option[Y]
required: Y
} yield x_name -> f(x_value, y.get(x_name))
^
这是可预测的,但我无法打开选项。
答案 0 :(得分:7)
以下是在Scala中编写此内容的非常简洁且合理的惯用方法:
def mergeWith[K, X, Y, Z](xs: Map[K, X], ys: Map[K, Y])
(f: (X, Y) => Z): Map[K, Z] =
xs.flatMap {
case (k, x) => ys.get(k).map(k -> f(x, _))
}
请注意,我使用的是Map
而不是HashMap
,并且我稍微更改了一些标识符名称。我还将该函数放入其自己的参数列表中,这可以使语法对用户来说更清晰。
这是非常密集的代码,所以我将解压缩一下。对于(k, v)
地图中的每个键值对xs
,如果ys.get(k)
中的密钥存在,我们会使用Option[Y]
来获得Some[Y]
ys
1}}和None
否则。我们使用map
上的Option
将Y
内的(K, Z)
值(如果存在)更改为ys.get(k).map(k -> f(x, _))
对。
整个Option[(K, Z)]
部分的值因此是map
,它是一对或零对的集合。如果我们在xs
上使用Seq[Option[(K, Z)]]
,我们最终会得到flatMap
,但由于我们使用了Map[K, Z]
,因此可以将其折叠为{ {1}},这就是我们想要的(以及我们指定的方法的返回类型。
我们可以尝试一下:
scala> val mapX = Map('a -> 1, 'b -> 2)
mapX: scala.collection.immutable.Map[Symbol,Int] = Map('a -> 1, 'b -> 2)
scala> val mapY = Map('b -> "foo", 'c -> "bar")
mapY: scala.collection.immutable.Map[Symbol,String] = Map('b -> foo, 'c -> bar)
scala> mergeWith(mapX, mapY) { (x, y) => (x, y) }
res0: Map[Symbol,(Int, String)] = Map('b -> (2,foo))
这就是我们想要的。