将 Seq[Any] 分解为 case class 的巧妙方法
Clever way to break a Seq[Any] into a case class
我一直在解析一种专有的文件格式,它有多个部分,每个部分都有许多记录。这些部分可以按任何顺序排列,记录也可以按任何顺序排列。顺序并不重要。虽然部分不应重复,但我不能保证。
我一直在使用 parboiled2 使用如下格式生成 AST:
oneOrMore( Section1 | Section2 | Section3 )
其中每个部分都会生成一个案例 class。它们不会继承任何结果 Seq[Any]
这些部分案例 classes 还包含特定于部分类型的 Seq[T]
记录。
我想将 Seq[Any]
转换成
case class (section1:Seq[T1], section2:Seq[T2], section3:Seq[T3] )
是否有人对此有聪明且易于阅读的技术,或者我应该制作一些可变集合并使用 foreach 进行匹配?
当我回到带有 vars 的 foreach 时,我总是觉得我缺少一些 Scala 魔法。
编辑 1:
有人提出我应该扩展一个公共基础class,我确实可以。但是如果我仍然必须使用 match
来识别类型,我看不出解决方案有什么变化。我想分离出不同大小写的 class 类型,例如下面我想收集所有 B
、C
、E
和 F
的组合成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) )
虽然这可以用 shapeless 来完成,以做出更简洁的解决方案(如 Travis ),但我选择了基于 Travis 反馈的纯 Scala 解决方案。
下面是一个使用 foldLeft 操作强类型元组外壳的示例 Seq[]
。不幸的是,每种可能的类型都需要在匹配中有一个案例,如果有很多类型,这可能会变得乏味。
另请注意,如果基数 class 是密封的,则在错过类型的情况下匹配将发出详尽警告,从而使此操作类型安全。
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) )
我一直在解析一种专有的文件格式,它有多个部分,每个部分都有许多记录。这些部分可以按任何顺序排列,记录也可以按任何顺序排列。顺序并不重要。虽然部分不应重复,但我不能保证。
我一直在使用 parboiled2 使用如下格式生成 AST:
oneOrMore( Section1 | Section2 | Section3 )
其中每个部分都会生成一个案例 class。它们不会继承任何结果 Seq[Any]
这些部分案例 classes 还包含特定于部分类型的 Seq[T]
记录。
我想将 Seq[Any]
转换成
case class (section1:Seq[T1], section2:Seq[T2], section3:Seq[T3] )
是否有人对此有聪明且易于阅读的技术,或者我应该制作一些可变集合并使用 foreach 进行匹配?
当我回到带有 vars 的 foreach 时,我总是觉得我缺少一些 Scala 魔法。
编辑 1:
有人提出我应该扩展一个公共基础class,我确实可以。但是如果我仍然必须使用 match
来识别类型,我看不出解决方案有什么变化。我想分离出不同大小写的 class 类型,例如下面我想收集所有 B
、C
、E
和 F
的组合成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) )
虽然这可以用 shapeless 来完成,以做出更简洁的解决方案(如 Travis
下面是一个使用 foldLeft 操作强类型元组外壳的示例 Seq[]
。不幸的是,每种可能的类型都需要在匹配中有一个案例,如果有很多类型,这可能会变得乏味。
另请注意,如果基数 class 是密封的,则在错过类型的情况下匹配将发出详尽警告,从而使此操作类型安全。
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) )