很多嵌套匹配...模式匹配的情况

时间:2013-09-17 03:51:41

标签: scala

我有一个自动生成的Web服务客户端。我有很多复杂的类,我必须对它进行模式匹配。现在我的结构看起来像这样:

val response = client.getResponse
response match {
  case Left(_) => None
  case Right(a: SomeClass) => a match {

    case SomeClass2(b: Option[SomeClass3]) => b match {

      case None => None
      case Some(c: SomeClass3) => c match {

        case SomeClass4(_, _, _, _, d: Seq[SomeClass4]) => d match {
          case Nil => None

          case seq: Seq[SomeClass5] => seq match {
            case Nil => None
            case Seq(xs@_*) => xs map { x =>
              x match {
                case Nil => None

                case SomeClass6(e: SomeClass7) => e match {
                  case Nil => None

                   case SomeClass8(f, _, _, _, _) => f match {
                    case Nil => None
                    case Seq(xs@_*) => xs map { x => 
                      x match {

                        case Nil => None
                        case SomeClass9(g: Seq[SomeClass9], _, _, _, _, _, _, _, _, _, _) => /* + some nested levels more*/
                    }
                  }
                }
              }
            }
          }
        }
      }
    }
  }
}

其中SomeClass1 - SomeClass9case个类。

正如你所看到的,它似乎令人恐惧。我该怎么办?什么是让它看起来更好的标准方法?

我想不仅应该进行重构,而应该采用另一种方法。

3 个答案:

答案 0 :(得分:6)

假设a应为SomeClass2,而不是SomeClass(与bcd相同。)

您可以使用case A | B => ...等替代模式和Some(MyClass(f))等结构模式。

您也可以在map中使用部分功能,例如map { case ... }而不是map { x => x match {...} }

我猜您的代码中存在错误:检查case Nil => ...; case SomeClass8(...) => ...

您可以将Seq(xs @_*)替换为xs。如果您需要整个集合,则无需提取元素。

您的代码:

response match {
  case Left(_) | Right(SomeClass2(None)) | Right(SomeClass2(Some(SomeClass3(_, _, _, _, Nil))) => None
  case Right(SomeClass2(Some(SomeClass3(_, _, _, _, xs))) =>
    xs map {
      case SomeClass6(None) | SomeClass6(Some(SomeClass8(Nil, _, _, _, _))) => None
      case SomeClass6(Some(SomeClass8(xs, _, _, _, _))) =>
        xs map {
          case Nil => None
          case SomeClass9(g, _, _, _, _, _, _, _, _, _, _) => /* + some nested levels more*/
        }
    }
}

您还应该将嵌套匹配提取到单独的方法。

模式匹配不是唯一的解决方案。您可以使用EitherOption

的方法
response.right.toOption.collect {
  // No need for the first part.
  case SomeClass2(Some(SomeClass3(_, _, _, _, xs)) if xs.nonEmpty => ...
}

答案 1 :(得分:5)

您可能会发现extractors有用。也可能值得将一些案例弄平,所以你有

case Right(SomeClass(SomeClass2(Some(SomeClass3(value))))) => value ...
case _ => None

而不是在每个级别明确定义None个案例。

答案 2 :(得分:2)

通过使用理解而不是模式匹配,你可以减少很多这种复杂性。

一个简单的机会是将序列映射到另一个模式匹配:

case seq: Seq[SomeClass5] => seq match {
  case Nil => None
  case Seq(xs@_*) => xs map { x =>
    x match {
       ...
    }
  }
}

这非常难看,因为您已使用match来消除Nil案例,然后再次匹配seq 两个match来处理一个对象。这可能会成为

case seq: Seq[SomeClass5] => for (x <- seq) yield {
  x match {
     ...
  }
}

这消除了Nil案例检查并删除了几层嵌套,这是一个巨大的胜利。你至少在两个层面上做到这一点,这是一个更大的胜利。当然,这会返回一个(可能Nil)序列,而不是f(x)None,但您可以轻松转换它。一种方法是在不添加嵌套的情况下执行此操作:

case seq: Seq[SomeClass5] => (for (x <- seq) yield {
  x match {
     ...
  }
}) match {
  case Nil => None
  case Seq(i) => Some(i)
  case ...
}

或者,如果(我怀疑)你希望这些序列只有一个元素......

case seq: Seq[SomeClass5] => (for (x <- seq) yield {
  x match {
     ...
  }
}) match {
  case Seq(i) => Some(i)
  case _ => None
}