Jim*_*Jim 11 scala scala-2.8 scala-collections
我达到了这个目的:
implicit def collectionExtras[A](xs: Iterable[A]) = new {
def zipWith[B, C, That](ys: Iterable[B])(f: (A, B) => C)(implicit cbf: CanBuildFrom[Iterable[A], C, That]) = {
val builder = cbf(xs.repr)
val (i, j) = (xs.iterator, ys.iterator)
while(i.hasNext && j.hasNext) {
builder += f(i.next, j.next)
}
builder.result
}
}
// collectionExtras: [A](xs: Iterable[A])java.lang.Object{def zipWith[B,C,That](ys: Iterable[B])(f: (A, B) => C)(implicit cbf: scala.collection.generic.CanBuildFrom[Iterable[A],C,That]): That}
Vector(2, 2, 2).zipWith(Vector(4, 4, 4))(_ * _)
// res3: Iterable[Int] = Vector(8, 8, 8)
Run Code Online (Sandbox Code Playgroud)
现在问题是上面的方法总是返回一个Iterable
.如何让它返回传递给它的类型集合?(在这种情况下,Vector
)谢谢.
你足够近了.只是两行中的微小变化:
implicit def collectionExtras[A, CC[A] <: IterableLike[A, CC[A]]](xs: CC[A]) = new {
def zipWith[B, C, That](ys: Iterable[B])(f: (A, B) => C)(implicit cbf: CanBuildFrom[CC[A], C, That]) = {
val builder = cbf(xs.repr)
val (i, j) = (xs.iterator, ys.iterator)
while(i.hasNext && j.hasNext) {
builder += f(i.next, j.next)
}
builder.result
}
}
Run Code Online (Sandbox Code Playgroud)
首先,您需要传递集合类型,因此我添加CC[A]
了一个类型参数.此外,该集合需要能够"重现"自身 - 这是由第二类参数保证的IterableLike
- 所以CC[A] <: IterableLike[A, CC[A]]
.请注意,这第二个参数IterableLike
是Repr
,恰恰类型xs.repr
.
当然,CanBuildFrom
需要接受CC[A]
而不是Iterable[A]
.这就是它的全部内容.
结果如下:
scala> Vector(2, 2, 2).zipWith(Vector(4, 4, 4))(_ * _)
res0: scala.collection.immutable.Vector[Int] = Vector(8, 8, 8)
Run Code Online (Sandbox Code Playgroud)
上面的问题是您的隐式转换collectionExtras
导致获取的对象丢失类型信息.特别是,在上面的解决方案中,具体的集合类型丢失了,因为你传递了一个类型的对象Iterable[A]
- 从这一点开始,编译器不再知道真正的类型xs
.尽管构建器工厂以CanBuildFrom
编程方式确保集合的动态类型是正确的(您真的得到了Vector
),但静态地,编译器只知道zipWith
返回的内容是一个Iterable
.
要解决这个问题,不要Iterable[A]
让隐式转换占用,而是让它采取IterableLike[A, Repr]
.为什么?
Iterable[A]
通常被声明为:
Iterable[A] extends IterableLike[A, Iterable[A]]
Run Code Online (Sandbox Code Playgroud)
与之不同的Iterable
是,它将IterableLike[A, Repr]
具体的集合类型保持为Repr
.大多数具体的收藏品,除了混合之外Iterable[A]
,还混合了特性IterableLike[A, Repr]
,用Repr
它们的混凝土类型代替,如下所示:
Vector[A] extends Iterable[A] with IterableLike[A, Vector[A]]
Run Code Online (Sandbox Code Playgroud)
他们可以这样做,因为type参数Repr
被声明为协变.
简而言之,使用IterableLike
隐式转换来保持具体的集合类型信息(即Repr
)并在定义时使用它zipWith
- 请注意,构建器工厂CanBuildFrom
现在将包含Repr
而不是Iterable[A]
第一个类型参数,从而导致适当的隐式对象得到解决:
import collection._
import collection.generic._
implicit def collectionExtras[A, Repr](xs: IterableLike[A, Repr]) = new {
def zipWith[B, C, That](ys: Iterable[B])(f: (A, B) => C)(implicit cbf: CanBuildFrom[Repr, C, That]) = {
val builder = cbf(xs.repr)
val (i, j) = (xs.iterator, ys.iterator)
while(i.hasNext && j.hasNext) {
builder += f(i.next, j.next)
}
builder.result
}
}
Run Code Online (Sandbox Code Playgroud)
更仔细地阅读你的问题公式("如何编写一个zipWith方法返回与传递给它的那些类型的集合?"),在我看来,你想拥有与传递给它的集合相同类型的集合zipWith
,而不是隐式转换,与...相同ys
.
原因与以前相同,请参阅以下解决方案:
import collection._
import collection.generic._
implicit def collectionExtras[A](xs: Iterable[A]) = new {
def zipWith[B, C, That, Repr](ys: IterableLike[B, Repr])(f: (A, B) => C)(implicit cbf: CanBuildFrom[Repr, C, That]) = {
val builder = cbf(ys.repr)
val (i, j) = (xs.iterator, ys.iterator)
while(i.hasNext && j.hasNext) {
builder += f(i.next, j.next)
}
builder.result
}
}
Run Code Online (Sandbox Code Playgroud)
结果如下:
scala> immutable.Vector(2, 2, 2).zipWith(mutable.ArrayBuffer(4, 4, 4))(_ * _)
res1: scala.collection.mutable.ArrayBuffer[Int] = ArrayBuffer(8, 8, 8)
Run Code Online (Sandbox Code Playgroud)
说实话,我不确定这是如何工作的:
implicit def collectionExtras[CC[X] <: Iterable[X], A](xs: CC[A]) = new {
import collection.generic.CanBuildFrom
def zipWith[B, C](ys: Iterable[B])(f: (A, B) => C)
(implicit cbf:CanBuildFrom[Nothing, C, CC[C]]): CC[C] = {
xs.zip(ys).map(f.tupled)(collection.breakOut)
}
}
scala> Vector(2, 2, 2).zipWith(Vector(4, 4, 4))(_ * _)
res1: scala.collection.immutable.Vector[Int] = Vector(8, 8, 8)
Run Code Online (Sandbox Code Playgroud)
基本上,我想使用CC[X]
类型构造函数来指示zipWith
应该返回集合类型xs
但是C
作为类型参数(CC[C]
).我想用它breakOut
来获得正确的结果类型.我希望有一个CanBuildFrom
隐含的范围,但后来得到这个错误信息:
required: scala.collection.generic.CanBuildFrom[Iterable[(A, B)],C,CC[C]]
Run Code Online (Sandbox Code Playgroud)
然后用诀窍Nothing
代替Iterable[(A, B)]
.我猜隐含在某个地方被定义了......
另外,我喜欢想你的zipWith
作为zip
,然后map
,让我改变了落实.以下是您的实施:
implicit def collectionExtras[CC[X] <: Iterable[X], A](xs: CC[A]) = new {
import collection.generic.CanBuildFrom
def zipWith[B, C](ys: Iterable[B])(f: (A, B) => C)
(implicit cbf:CanBuildFrom[Nothing, C, CC[C]]) : CC[C] = {
val builder = cbf()
val (i, j) = (xs.iterator, ys.iterator)
while(i.hasNext && j.hasNext) {
builder += f(i.next, j.next)
}
builder.result
}
}
Run Code Online (Sandbox Code Playgroud)
请注意,本文提供了类型构造函数模式的一些背景知识.