Scala多级模式匹配

时间:2017-05-22 21:23:52

标签: scala pattern-matching

我坚持使用多级模式匹配,在下面的代码中我想匹配一个特定情况,在几个级别检查" cfe是Assignment,assignmentCfe.getRight是BinaryExpression,等等& #34;,解决方案看起来很难看,我希望Scala能为我提供更好的东西。 :)

  def findAll(cfg: Cfg, consumer: Consumer[Violation]): Unit = {
    val fa = new FlowAnalyzer
    val states = fa.analyze(cfg)

    states.foreach { case (cfe, state) => cfe match {
      case assign: Assignment => assign.getRight match {
        case expression: BinaryExpression => expression.getOperator match {
          case Operator.Div | Operator.Rem => processDivisions()
          case _ =>
        }
        case _ =>
      }
      case _ =>
    }
    case _ =>
    }
  }

如何摆脱这些空的默认情况?

另一种方法是使用嵌套条件,但IntelliJ IDEA让我将这些条件替换回模式匹配

states.foreach { case (cfe, state) => if (cfe.isInstanceOf[Assignment]) {
      val assignment = cfe.asInstanceOf[Assignment]
      if (assignment.getRight.isInstanceOf[BinaryExpression]) {
        val expression = assignment.getRight.asInstanceOf[BinaryExpression]
        if (expression.getOperator == Operator.Div || expression.getOperator == Operator.Rem) processDivisions()
      }
    }}

3 个答案:

答案 0 :(得分:3)

AssignmentBinaryExpression本身是案例类吗?或者他们有相应的unapply方法?如果是这样,那么您可以嵌套模式匹配并忽略您不关心的字段。例如,像:

def findAll(cfg: Cfg, consumer: Consumer[Violation]): Unit = {
  val fa = new FlowAnalyzer
  val states = fa.analyze(cfg)

  states.foreach {
    case (Assignment(_, BinaryExpression(_, _, Operator.Div | Operator.Rem)), _) => processDivisions()
    case _ =>
  }
}

这至少会将默认匹配的数量减少到1。

如果这些不是案例类或没有提取器,那么如果这是代码中常见的(反)模式,则可以考虑编写自己的类:http://docs.scala-lang.org/tutorials/tour/extractor-objects.html

答案 1 :(得分:0)

另一个想法是你可以使用“pimp my library”模式来定义从任何对象到可以进行某种部分匹配的类的隐式转换:

class PartialMatcher[A](a: A) {
  def partialMatch(f: PartialFunction[A, Unit]): Unit = if (f.isDefinedAt(a)) f(a)
}
implicit def makePartialMatcher[A](a: A) = new PartialMatcher(a)

然后用partialMatch替换所有这些匹配:

  def findAll(cfg: Cfg, consumer: Consumer[Violation]): Unit = {
    val fa = new FlowAnalyzer
    val states = fa.analyze(cfg)

    states.foreach { case (cfe, state) => cfe partialMatch {
      case assign: Assignment => assign.getRight partialMatch {
        case expression: BinaryExpression => expression.getOperator partialMatch {
          case Operator.Div | Operator.Rem => processDivisions()
        }
      }
    }}
  }

请注意,还有其他原因可以避免这种情况......过度使用隐式转换会使理解代码变得更加困难。这是一种风格选择。

答案 2 :(得分:0)

使用.collect

def findAll(cfg: Cfg, consumer: Consumer[Violation]): Unit = {
  val fa = new FlowAnalyzer
  val states = fa.analyze(cfg)
  states.collect { case (assign: Assignment, _) => 
    assign.getRight
  }.collect { case expression: BinaryExpression => 
    expression.getOperator
  }.collect { case Operator.Div | Operator.Rem => 
    processDivisions
  }