巧妙地将Seq [Any]分解为案例类

时间:2016-03-08 18:30:43

标签: scala seq parboiled2

我一直在解析具有部分的专有文件格式,每个部分都有许多记录。这些部分可以是任何顺序,记录可以是任何顺序。订单并不重要。虽然不应重复部分,但我无法保证。

我一直在使用parboiled2来生成AST,使用如下格式:

oneOrMore( Section1 | Section2 | Section3 )

每个部分都会生成一个案例类。它们不会继承任何导致Seq[Any]

的内容

这些部分案例类还包含特定于部分类型的Seq[T]个记录。

我想将Seq[Any]转换为

case class (section1:Seq[T1], section2:Seq[T2], section3:Seq[T3] )

有人有一个聪明且易于阅读的技术,或者我应该制作一些可变的集合并使用匹配的foreach?

当我回到战车的时候,我总觉得我错过了一些Scala魔法。

编辑1: 提出我应该扩展一个共同的基类,我确实可以。但是,如果我仍然需要使用match来识别类型,我不会看到解决方案的变化。我想分出不同的案例类类型,例如下面我要收集所有BCEF一起变成Seq[B]Seq[C]Seq[E]Seq[F]

 class A()
 case class B(v:Int) extends A
 case class C(v:String) extends A

 case class E(v:Int)
 case class F(v:String)

 val a:Seq[A] = B(1) :: C("2") :: Nil
 val d:Seq[Any] = E(3) :: F("4") :: Nil

 a.head match {
   case B(v) => v should equal (1)
   case _ => fail()
 }

 a.last match {
   case C(v) => v should equal ("2")
   case _ => fail()
 }

 d.head match {
   case E(v) => v should equal (3)
   case _ => fail()
 }

 d.last match {
   case F(v) => v should equal ("4")
   case _ => fail()
 }

编辑2:折叠解决方案

  case class E(v:Int)
  case class F(v:String)


  val d:Seq[Any] = E(3) :: F("4") :: Nil

  val Ts = d.foldLeft((Seq[E](), Seq[F]()))(
    (c,r) => r match {
      case e:E => c.copy(_1=c._1 :+ e)
      case e:F => c.copy(_2=c._2 :+ e)
    }
  )

  Ts should equal ( (E(3) :: Nil,  F("4") :: Nil) )

编辑3:穷举

  sealed trait A //sealed is important
  case class E(v:Int) extends A
  case class F(v:String) extends A


  val d:Seq[Any] = E(3) :: F("4") :: Nil

  val Ts = d.foldLeft((Seq[E](), Seq[F]()))(
    (c,r) => r match {
      case e:E => c.copy(_1=c._1 :+ e)
      case e:F => c.copy(_2=c._2 :+ e)
    }
  )

  Ts should equal ( (E(3) :: Nil,  F("4") :: Nil) )

1 个答案:

答案 0 :(得分:0)

虽然这可以通过无形的方式来制作更简洁的解决方案(As Travis pointed out),但我选择使用基于Travis的纯Scala解决方案'反馈

以下是使用foldLeft操作强类型Seq[]的元组外壳的示例。不幸的是,每种可能的类型都需要匹配中的一个案例,如果有很多类型,这可能会变得乏味。

另请注意,如果基类是密封的,那么匹配将在错过类型的情况下发出穷举警告,使此操作类型安全。

  sealed trait A //sealed is important
  case class E(v:Int) extends A
  case class F(v:String) extends A


  val d:Seq[A] = E(3) :: F("4") :: Nil

  val Ts = d.foldLeft((Seq[E](), Seq[F]()))(
    (c,r) => r match {
      case e:E => c.copy(_1=c._1 :+ e)
      case e:F => c.copy(_2=c._2 :+ e)
    }
  )

  Ts should equal ( (E(3) :: Nil,  F("4") :: Nil) )
相关问题