如何使用我自己的通用地图丰富TraversableOnce?

时间:2014-02-07 19:00:40

标签: scala

我正在尝试丰富所有TraversableOnce [String]对象,我无法找出构建迭代器的正确语法。这就是我到目前为止所做的:

class Exclaimer[R <: TraversableOnce[String]](val lines:R) {
  import scala.collection.generic.CanBuildFrom
  def exclaim(implicit bf:CanBuildFrom[R,String,R]):R = {
    val b = bf(lines)
    lines.foreach(b += _)
    b.result
  }
}
implicit def WrapExclaimer[R <: TraversableOnce[String]](lines:R) = new Exclaimer(lines)

它适用于集合(即它返回与我给它相同的类的集合),但它不适用于迭代器,因为它Cannot construct a collection of type Iterator[java.lang.String] with elements of type String based on a collection of type Iterator[java.lang.String]。我该如何解决?我正在使用Scala 2.9(编辑:我错误地写了2.8)。

以下是一些示例输出:

scala> List("a","b","c").exclaim
res5: List[java.lang.String] = List(a, b, c)

scala> Vector("a","b","c").exclaim
res6: scala.collection.immutable.Vector[java.lang.String] = Vector(a, b, c)

scala> List("a","b","c").iterator.exclaim
<console>:10: error: Cannot construct a collection of type Iterator[java.lang.String] with elements of type String based on a collection of type Iterator[java.lang.String].
              List("a","b","c").iterator.exclaim
                                         ^

2 个答案:

答案 0 :(得分:5)

2.10

的一般解决方案

您应该使用exclaim[That](implicit bf:CanBuildFrom[R, String, That])代替CanBuildFrom[R,String,R]。另请注意,有更多通用方法可以扩展类似集合的类 - IsTraversableOnce(以及IsTraversableLike

import collection.generic.IsTraversableOnce
import collection.GenTraversableOnce

class Exclaimer[A, Repr](val lines: GenTraversableOnce[A]) {
  import scala.collection.generic.CanBuildFrom
  def exclaim[That](implicit bf:CanBuildFrom[Repr, String, That], e: A =:= String): That = {
    val b = bf()
    lines.foreach(s => b += e(s))
    b.result
  }
}
implicit def wrapExclaimer[Repr](r: Repr)(implicit fr: IsTraversableOnce[Repr]): Exclaimer[fr.A,Repr] =
  new Exclaimer[fr.A, Repr](fr.conversion(r))

此方法适用于Array

Array("a","b","c").exclaim
// Array[String] = Array(a, b, c)

修正了初始实施

这是您的初始实施(已修复)。它与Iterator一起使用,但在Array上失败,因为Array不是TraversableOnce

class Exclaimer[R <: TraversableOnce[String]](val lines:R) {
  import scala.collection.generic.CanBuildFrom
  def exclaim[That](implicit bf:CanBuildFrom[R,String,That]):That = {
    val b = bf(lines)
    lines.foreach(b += _)
    b.result
  }
}
implicit def WrapExclaimer[R <: TraversableOnce[String]](lines:R) = new Exclaimer(lines)

scala> List("a","b","c").iterator.exclaim
res0: Iterator[String] = non-empty iterator

scala> Array("a","b","c").exclaim
<console>:10: error: value exclaim is not a member of Array[String]
              Array("a","b","c").exclaim
                                 ^

CanBuildFrom

IsTraversableOnce scala中没有2.9.3,因此您必须使用初始方法的固定版本。但是,您将获得TraversableOnce而不是Iterator

List("a","b","c").iterator.exclaim
// scala.collection.TraversableOnce[String] = non-empty iterator

要获得Iterator,您必须像这样创建自己的隐式CanBuildFrom

import collection.generic.CanBuildFrom
import collection.mutable.Builder
import collection.immutable.VectorBuilder

implicit def iteratorCbf[A, B] = new CanBuildFrom[Iterator[A], B, Iterator[B]]{
  def apply(): Builder[B, Iterator[B]] = new Builder[B, Iterator[B]]{
    private[this] val inner = new VectorBuilder[B]
    def +=(elem: B) = {
      inner += elem
      this
    }
    def clear(): Unit = inner.clear()
    def result(): Iterator[B] = inner.result().iterator
  }
  def apply(i: Iterator[A]) = apply()
}

不,您将获得Iterator[String]而不是TraversableOnce[String]

List("a","b","c").iterator.exclaim
// Iterator[String] = non-empty iterator

您应该将隐式iteratorCbf方法添加到Exclaimer类的随播广告对象中。

答案 1 :(得分:0)

我认为最干净的答案是不要尝试使用通用地图来丰富TraversableOnce。 TraversableOnce的重点在于它涵盖迭代器和集合 - 但是通常丰富TraversableOnce(使用Scala 2.10)的唯一方法是将迭代器转换为集合并返回到迭代器,这会使迭代器的点失效(或者至少有一点:能够流式传输大型数据集,而无需将它们全部加载到内存中。)

我天真地认为,因为Iterator.map和Collection.map具有相同的语法,所以它们有一个共同的实现,所以我试图找到我的Exclaimer地图的常见实现。但是,看看Scala源代码,我们看到Iterator.map和Collection.map有不同的实现:

// Implementation of map in TraversableOnce[+A] implicit typecast to MonadOps[+A]
def map[B](f: A => B): TraversableOnce[B] = trav.toIterator map f

// Implementation of map in Iterator[+A]
def map[B](f: A => B): Iterator[B] = new AbstractIterator[B] {
  def hasNext = self.hasNext
  def next() = f(self.next())
}

// Implementation of map in TraversableLike[+A, +Repr]
def map[B, That](f: A => B)(implicit bf: CanBuildFrom[Repr, B, That]): That = {
  def builder = { // extracted to keep method size under 35 bytes, so that it can be JIT-inlined
    val b = bf(repr)
    b.sizeHint(this)
    b
  }
  val b = builder
  for (x <- this) b += f(x)
  b.result
}

道德:如果Scala设计师决定不为TraversableOnce编写通用的地图函数,那么我也不应该尝试。

这是我最终得到的代码,感谢@senia。

class TraversableExclaimer[R <: Traversable[String]](val lines:R) {
  import scala.collection.generic.CanBuildFrom
  def exclaim[That](implicit bf:CanBuildFrom[R,String,That]):That = {
    val b = bf(lines)
    lines.foreach(b += _+"!")
    b.result
  }
}
implicit def WrapTraversableExclaimer[R <: Traversable[String]](lines:R) = new TraversableExclaimer[R](lines)

class IteratorExclaimer[T <: String](val lines:Iterator[T]) {
  def exclaim:Iterator[T] = new Object with Iterator[T] {
    def hasNext = lines.hasNext
    def next() = (lines.next()+"!").asInstanceOf[T]
  }
}
implicit def WrapIteratorExclaimer(lines:Iterator[String]) = new IteratorExclaimer[String](lines)