是否有按元素类型过滤和映射集合的内置更优雅的方法?

时间:2009-09-28 17:18:17

标签: scala types scala-collections

如果我想缩小某个特定类型的所有元素Iterable[A](例如String),我可以这样做:

as filter { _.isInstanceOf[String] }

但是,显然希望使用作为Iterable[String]这可以通过map完成:

as filter { _.isInstanceOf[String] } map { _.asInstanceOf[String] }

哪个很难看。当然,我可以使用flatMap代替:

as flatMap[String] { a => 
  if (a.isInstanceOf[String]) 
    Some(a.asInstanceOf[String]) 
  else
    None
}

但我不确定这是否更具可读性!我编写了一个函数narrow,可以通过implicit次转换使用:

as.narrow(classOf[String])

但我想知道是否有一个我忽略的更好的内置机制。特别是因为能够将List[A]缩小到List[String],而不是像我的函数那样缩小到Iterable[String]会很好。

4 个答案:

答案 0 :(得分:8)

isInstanceOf / asInstanceOf的Scala语法糖是模式匹配:

as flatMap { case x: String => Some(x); case _ => None }

因为它使用flatMap,所以它通常应该返回您必须开始的相同集合。

在Scala 2.8上,有一个实验函数可以在对象PartialFunction中定义这种模式。所以,在Scala 2.8上你可以做到:

as flatMap (PartialFunction.condOpt(_ : Any) { case x: String => x })

哪个看起来更大,主要是因为我没有先导入该功能。但是,再一次,在Scala 2.8上有更直接的方法:

as collect { case x: String => x }

答案 1 :(得分:3)

记录中,这是narrow的完整实现。与问题中给出的签名不同,它使用隐式Manifest来避免某些字符:

implicit def itrToNarrowSyntax[A](itr: Iterable[A]) = new {
  def narrow[B](implicit m: Manifest[B]) = {
    itr flatMap { x => 
      if (Manifest.singleType(x) <:< m)
        Some(x)
      else
        None
    }
  }
}

val res = List("daniel", true, 42, "spiewak").narrow[String]  
res == Iterable("daniel", "spiewak")

不幸的是,缩小到特定类型(例如List[String])而不是Iterable[String]有点困难。可以使用Scala 2.8.0中的新集合API通过利用更高类型来完成它,但不能在当前框架中完成。

答案 2 :(得分:1)

您可以在将来使用:

for(a :Type <- itr) yield a

但它现在不起作用。 有关更多信息,请转到以下链接: http://lampsvn.epfl.ch/trac/scala/ticket/1089 http://lampsvn.epfl.ch/trac/scala/ticket/900

答案 3 :(得分:1)

保持形状:我现在有点匆忙,所以我要离开那里的演员,但我很确定它可以被淘汰。这适用于主干:

import reflect.Manifest
import collection.Traversable
import collection.generic.CanBuildFrom
import collection.mutable.ListBuffer

object narrow {
  class Narrower[T, CC[X] <: Traversable[X]](coll: CC[T])(implicit m1: Manifest[CC[T]], bf: CanBuildFrom[CC[T], T, CC[T]]) {
    def narrow[B: Manifest]: CC[B] = {
      val builder = bf(coll)      
      def isB(x: T): Option[T] = if (Manifest.singleType(x) <:< manifest[B]) Some(x) else None

      coll flatMap isB foreach (builder += _)
      builder mapResult (_.asInstanceOf[CC[B]]) result
    }
  }

  implicit def toNarrow[T, CC[X] <: Traversable[X]](coll: CC[T])(implicit m1: Manifest[CC[T]], bf: CanBuildFrom[CC[T], T, CC[T]]) = 
    new Narrower[T,CC](coll)

  def main(args: Array[String]): Unit = {
    println(Set("abc", 5, 5.5f, "def").narrow[String])
    println(List("abc", 5, 5.5f, "def").narrow[String]) 
  }
}

运行它:

Set(abc, def)
List(abc, def)