我正在尝试丰富所有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
^
答案 0 :(得分:5)
您应该使用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
^
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)