【问题标题】:Scala pattern matching _ symbolScala模式匹配_符号
【发布时间】:2015-10-07 14:44:09
【问题描述】:

我是 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 在做什么吗? 如果有人想深入解释所有代码也可以,因为我正在尝试处理函数式编程。

干杯!

【问题讨论】:

  • 你问的是_还是@?标题说明了一件事,但帖子说明了另一件事。
  • @Rex Kerr,这还不清楚。我想我的问题应该是:case _ => acc 如何与(acc @ (deltaBefore, deltaAfter) 交互。我研究并发现了一个post,它解释了@ 符号。但在这种情况下我无法理解它。
  • (acc @ (deltaBefore, deltaAfter) 匹配两个 Map 的元组。元组被提取,其内容绑定到deltaBeforedeltaAfter。但是,在代码中,您还需要将元组本身按原样传递给下一次迭代。您还可以使用 @ 将元组本身绑定到本地 val。所以上面的情况也可以写成case acc => val (deltaBefore, deltaAfter) = acc ...。更新我的答案以包括其中的一些内容。
  • @Sascha,谢谢你的澄清。

标签: scala


【解决方案1】:

每个模式都匹配case _ =>。这就像if-else 中的else 或java 的switch 块中的default 情况。 如果(mapBefore(key), mapAfter(key)) 不匹配,则在您的代码中

(beforeMap: Map[_, _], afterMap: Map[_, _])

(beforeValue, afterValue) if beforeValue != afterValue

它将始终与_ 匹配,并且将执行case _ => 之后的代码。

【讨论】:

    【解决方案2】:

    首先提出问题,虽然我不明白标题中的@ 应该是什么意思。

    _ 是 scala 中的通配符。所以,在上面的代码中

    case _ => acc
    

    匹配所有与之前的情况不匹配的情况。在上面的代码中,来自 mapBeforemapAfter 的所有值都具有相同的键,它们没有区别。因此,前一个折叠循环的结果会被保留,而在其他情况下,会添加不同的值对。

    现在关于其余的代码(我不是作者,但我可以猜到):

    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 获取一个起始值,然后遍历已调用它的集合。对于集合的每个项目,最后一步的结果和集合的当前元素作为 fold 函数的输入给出。在第一步中,起始值替换结果。

    函数调用可以重写为:

    sameKeys.foldLeft(startAcc){ inputTuple: ((JsonMap, JsonMap), String) =>
    

    但是,由于您必须以相当繁琐且难以阅读的方式访问元组内容(例如 inputTuple._1._2 以获取先前结果的第二个映射),因此开发人员利用了我们可以直接匹配 fold、map、flatMap 等函数的输入。因此

    case (acc @ (deltaBefore, deltaAfter), key) =>
    

    将两个元组的内容绑定到可读的本地 vals 并将元组本身绑定到 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))
    

    匹配本身为Maps 的值并应用递归函数来获取这些映射的所有差异并将它们添加到此折叠步骤的结果中。

    case (beforeValue, afterValue) if beforeValue != afterValue =>
        (deltaBefore + (key -> beforeValue), deltaAfter + (key -> afterValue))
    

    匹配两个输入映射中相等的所有其他值,并将它们添加到此折叠步骤的结果中。

    所以最后你应该得到两个具有相同 keySet 的 Maps 以及原始两个输入映射中不同的所有值。

    【讨论】:

    • 美丽的解释。谢谢。
    • 呵呵,你把我在另一个问题中写的代码解释得很好!
    猜你喜欢
    • 1970-01-01
    • 2014-01-11
    • 2018-06-04
    • 2016-01-08
    • 1970-01-01
    • 1970-01-01
    • 2015-06-07
    • 2016-05-17
    • 2021-12-17
    相关资源
    最近更新 更多