使用收集来保存类型信息

时间:2017-09-05 08:20:17

标签: scala generics collections scala-collections

我对Traversable有以下扩展名:

import scala.reflect.ClassTag

object ContainerHelpers {

  implicit class TraversableOps[X](a: Traversable[X]) {
    def partitionByType[T <: X: ClassTag]: (Traversable[T], Traversable[X]) = {
      val (typed, other) = a.partition {
        case _: T => true
        case _ => false
      }
      (typed.map(_.asInstanceOf[T]), other)
    }

  }

  // test case:
  trait T
  case class A(i: Int) extends T

  val s = Seq[T](A(0), A(1))
  val sa = s.partitionByType[A]
  sa // I would like this to be Seq[A], not Traversable[A]

}

扩展程序正常,但它不保留集合类型 - 它总是返回Traversable。我想写一下,以便partitionByType使用partitionmap已知的类型信息,因此sa的类型应为(Seq[A], Seq[T]),而不是(Traversable[A], Traversable[T]) {1}}。我怎么能这样做,也许使用更高级的类型,或类似的东西?

2 个答案:

答案 0 :(得分:3)

您可以使用CanBuildFrom并自行构建集合,而不是依赖TraversableLike的实现,然后将其丢弃。这个  具有不在任何地方进行任何投射的优势,并且除了替代方案之外,它不会花费您更多,因为partition中的TraversableLike方法也是如此。

这是第一次获得你想要的东西(为了清晰起见我更改了参数化类型的名称)

implicit class TraversableOps[Elem, T[_Ignore] <: Traversable[_Ignore]](a: T[Elem]) {
  def partitionByType[Target <: Elem: ClassTag]
     (implicit cbf1: CanBuildFrom[T[Elem], Elem, T[Elem]],
       cbf2: CanBuildFrom[T[Target], Target, T[Target]]) : (T[Target], T[Elem]) = {

    val l = cbf2()
    val r = cbf1()

    for (x <- a) x match{
      case t: Target => l += t
      case _ => r += x
    }

    (l.result, r.result)
 }
}

它适用于以下测试用例:

// test case:
trait A
trait B extends A
case class T(i: Int) extends A
case class U(i: Int) extends B

val s1 : List[A] = List(T(0), U(1))
val s2 : Seq[A] = List(T(0), U(1))

val sa1 = s1.partitionByType[T]  //sa1 : (List[T], List[A]) = (List(T(0)),List(U(1)))
val sa2 = s2.partitionByType[T]   //sa2 : (Seq[T], Seq[A]) = (List(T(0)),List(U(1)))

答案 1 :(得分:2)

使用 F-bounded容器执行此操作,例如:

  //F[_] extend from Traversable for own the original type of colleciton
  implicit class TraversableOps[X, F[_] <: Traversable[_]](a: F[X]) {
    def partitionByType[T <: X : ClassTag]: (F[T], F[X]) = {
      //cast to Traversable for known partition type 
      val (typed, other) = a.asInstanceOf[Traversable[X]].partition {
        case _: T => true
        case _ => false
      }
      //cast to the target type, Since the original is F-bounded, so when cast also is cast type safe
      (typed.asInstanceOf[F[T]], other.asInstanceOf[F[X]])
    }

  }

  // test case:
  trait T

  case class A(i: Int) extends T

  case class B(i: Double) extends T

  val s = Seq[T](A(0), A(1), B(2.0f))
  val sa: (Seq[A], Seq[T]) = s.partitionByType[A]
  println(sa._1)
  println(sa._2)