【问题标题】:scala filter by typescala按类型过滤
【发布时间】:2015-04-27 02:30:54
【问题描述】:

我已阅读TypeTag相关文章,但无法实现按元素类型过滤集合。

例子:

trait A
class B extends A
class C extends A

val v = Vector(new B,new C)
v filter ( _.isInstanceOf[B] )

上面的代码运行良好。 但是我想从v 中提取filter。例如

def filter[T,T2](data:Traversable[T2]) = (data filter (  _.isInstanceOf[T])).asInstanceOf[Traversable[T]]

//Then filter v by
filter[B,A](v)

在这种情况下,我会收到警告 abstract type T is unchecked since it is eliminated by erasure。我尝试使用TypeTag,但在运行时获取Type 似乎并不容易。

有没有什么优雅的方案来实现filter功能? 任何通过 scala 宏的解决方案也是可以接受的。

【问题讨论】:

    标签: scala generics runtime-type


    【解决方案1】:

    您需要提供ClassTag,而不是TypeTag,并使用模式匹配。 ClassTags 与模式匹配配合得很好。您甚至可以使用collect 方法同时执行filtermap

    def filter[T, T2](data: Traversable[T2])(implicit ev: ClassTag[T]) = data collect {
        case t: T => t
    }
    

    例如:

    val data = Seq(new B, new B, new C, new B)
    filter[B, A](data) //Traversable[B] with length 3
    filter[C, A](data) //Traversable[C] with length 1
    

    这样做的一个问题是它可能无法按预期使用嵌套泛型类型。

    collect 方法接受PartialFunction 类型的参数,表示不需要在整个域上定义的函数。当使用 collect 未定义 PartialFunction 的元素时,将被过滤掉,并相应地映射与某些 case 语句匹配的元素。

    您也可以使用existential types 并让编译器推断data 参数的类型以获得更简洁的语法。你也可以使用context bounds:

    def filter[T : ClassTag](data: Traversable[_]) = data collect { case t: T => t }
    filter[B](data)
    

    这里的方法的一个问题是,您拥有的本机 filter 方法之间存在显着差异:这些方法总是返回 Traversable,而本机 filter 返回它可以的最佳类型。例如:

    val data = Vector(new B, new B, new C, new B)
    data filter { _.isInstanceOf[B] } //Vector[A]
    data filter { _.isInstanceOf[B] } map { _.asInstanceOf[B] } //Vector[B]
    data collect { case t: B => t } //Vector[B].  Note that if you know the type at the calling point, this is pretty concise and might not need a helper method at all
    
    //As opposed to:
    filter[B](data) //Traversable[B], not a Vector!
    

    您可以通过使用另一个隐式参数的CanBuildFrom 模式来解决此问题。您也可以使用implicit classes 实质上将方法添加到类中(与上面显示的静态样式调用方法相反)。所有这些加起来是一个相当复杂的方法,但如果您对这些增强功能感兴趣,我将把它留在这里:

    implicit class RichTraversable[T2, Repr <: TraversableLike[T2, Repr], That](val trav: TraversableLike[T2, Repr]) extends AnyVal {
        def withType[T : ClassTag](implicit bf: CanBuildFrom[Repr, T, That]) = trav.collect {
            case t: T => t
        }
    }
    

    这将允许您这样做:

    data.withType[B] //Vector[B], as desired    
    

    【讨论】:

    • 嗨@BenReich,谢谢你的好回答。你能解释一下data collect { case t: B =&gt; t }的语法{ case t: B =&gt; t }吗?为什么不需要在那里提供详尽的模式匹配?
    • @worldterminator 我添加了一个小编辑,解释了collect 一点点。
    • @BenReich 谢谢,最后一段代码正是我需要的。它也是 Scala 的可能性以及其类型系统和集合库的难度的完美示例。我希望尽快掌握这样的东西。
    【解决方案2】:

    肯定有更简单的方法。下面的工作,但我什至希望有一个更简单的解决方案

    trait A
    case class B()
    case class C()
    import scala.reflect.runtime.universe._
    import scala.util.Try
    val m = runtimeMirror(getClass.getClassLoader)
    def filter[T](data:Traversable[_])(implicit t:TypeTag[T]) = data collect {
          case i if Try(i.getClass.asSubclass(m.runtimeClass(typeOf[T].typeSymbol.asClass))).isSuccess => i.asInstanceOf[T]
        }
    val v= Vector(new B,new C)
    
    scala>     println(filter[B](v))
    Vector(B()) //This gets printed
    

    【讨论】:

      猜你喜欢
      • 2011-04-09
      • 2011-04-04
      • 2019-06-03
      • 2011-01-14
      • 2014-12-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多