我是Scala的新手。在我之前的post中,有人提供了一段我无法正确理解的代码。
type JsonMap = Map[String, Any]
def getMapDiffs(mapBefore: JsonMap, mapAfter: JsonMap) : (JsonMap, JsonMap) = {
val sameKeys = mapBefore.keySet intersect mapAfter.keySet
val startAcc = (Map.empty[String, Any], Map.empty[String, Any])
sameKeys.foldLeft(startAcc){ case (acc @ (deltaBefore, deltaAfter), key) =>
(mapBefore(key), mapAfter(key)) match {
// two maps -> add map diff recursively to before diff and after diff
case (beforeMap: Map[_, _], afterMap: Map[_, _]) =>
val (deltaB, deltaA) =
getMapDiffs(beforeMap.asInstanceOf[JsonMap], afterMap.asInstanceOf[JsonMap])
(deltaBefore + (key -> deltaB), deltaAfter + (key -> deltaA))
// values before and after are different
// add values to before diff and after diff
case (beforeValue, afterValue) if beforeValue != afterValue =>
(deltaBefore + (key -> beforeValue), deltaAfter + (key -> afterValue))
// keep existing diff
case _ => acc
}
}
}
有人可以向我解释case _ => acc
正在做什么吗?
如果有人想要解释所有深入的代码也会起作用,因为我试图掌握函数式编程。
干杯!
答案 0 :(得分:5)
首先回答这个问题,虽然我没有得到标题中@
的含义。
_
是scala中的通配符。所以,在上面的代码中
case _ => acc
匹配以前案例不匹配的所有案例。在上面的代码中,来自 mapBefore 和 mapAfter 的所有值都使用相同的密钥,这些值没有区别。因此保留了前一个折叠周期的结果,而在其他情况下,添加了不同值的对。
现在关于代码的其余部分(我不是作者,但我可以猜到):
type JsonMap = Map[String, Any]
是为了简洁和方便而定义的类型别名。
def getMapDiffs(mapBefore: JsonMap, mapAfter: JsonMap) : (JsonMap, JsonMap) = {
val sameKeys = mapBefore.keySet intersect mapAfter.keySet
val startAcc = (Map.empty[String, Any], Map.empty[String, Any])
samekeys
是在两个输入映射中定义的一组键
startAcc
是即将到来的foldLeft的起始值。
sameKeys.foldLeft(startAcc){ case (acc @ (deltaBefore, deltaAfter), key) =>
foldLeft
获取一个起始值,然后遍历调用它的集合。对于集合的每个项目,最后一步的结果和集合的当前元素作为折叠函数的输入给出。在第一步中,起始值将替换结果。
函数调用可以重写为:
sameKeys.foldLeft(startAcc){ inputTuple: ((JsonMap, JsonMap), String) =>
但是,因为您必须以相当繁琐且难以读取的方式访问元组内容(例如,输入上一个结果的第二个映射的inputTuple._1._2),开发人员利用我们可以直接的事实匹配fold,map,flatMap等功能的输入。因此
case (acc @ (deltaBefore, deltaAfter), key) =>
将两个元组的内容绑定到可读的本地val,将元组本身绑定到acc(这就是@的用途)。这样我们可以方便地使用两个映射以及最后一次迭代的完整结果元组,这在默认情况下特别有用(case _ => acc
)
(mapBefore(key), mapAfter(key)) match {
从两个输入映射中检索相同当前键的值并开始匹配。
case (beforeMap: Map[_, _], afterMap: Map[_, _]) =>
val (deltaB, deltaA) =
getMapDiffs(beforeMap.asInstanceOf[JsonMap], afterMap.asInstanceOf[JsonMap])
(deltaBefore + (key -> deltaB), deltaAfter + (key -> deltaA))
匹配本身为Map
的值,并应用递归函数来获取这些映射的所有差异,并将它们添加到此折叠步骤的结果中。
case (beforeValue, afterValue) if beforeValue != afterValue =>
(deltaBefore + (key -> beforeValue), deltaAfter + (key -> afterValue))
匹配 NOT 相等的两个输入映射的所有其他值,并将它们添加到此折叠步骤的结果中。
所以最后你应该得到两个具有相同keySet的地图,以及原始两个输入地图中不同的所有值。
答案 1 :(得分:2)
每个模式都与else
匹配。它与if-else
中的default
或java switch
块中的(mapBefore(key), mapAfter(key))
情况类似。
如果(beforeMap: Map[_, _], afterMap: Map[_, _])
与
(beforeValue, afterValue) if beforeValue != afterValue
或
_
它将始终与case _ =>
匹配,并且{{1}}之后的代码将被执行。