如何编写一个 zipWith 方法,该方法返回与传递给它的集合类型相同的集合?
我已经到达这里了:
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)
现在的问题是上面的方法总是返回一个Iterable
。如何让它返回传递给它的类型集合? (在本例中为Vector
)谢谢。
I have reached this far:
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)
Now the problem is that above method always returns an Iterable
. How do I make it return the collection of type as those passed to it? (in this case, Vector
) Thanks.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(3)
你已经足够接近了。只需两行的小改动:
首先,您需要获取传递的集合类型,因此我添加了
CC[A]
作为类型参数。此外,该集合需要能够“复制”自身——这是由 IterableLike 的第二个类型参数保证的——所以 CC[A] <: IterableLike[A, CC [A]]。请注意,IterableLike
的第二个参数是Repr
,正是xs.repr
的类型。当然,
CanBuildFrom
需要接收CC[A]
而不是Iterable[A]
。这就是全部内容。结果:
You got close enough. Just a minor change in two lines:
First, you need to get the collection type being passed, so I added
CC[A]
as a type parameter. Also, that collection needs to be able to "reproduce" itself -- that is guaranteed by the second type parameter ofIterableLike
-- soCC[A] <: IterableLike[A, CC[A]]
. Note that this second parameter ofIterableLike
isRepr
, precisely the type ofxs.repr
.Naturally,
CanBuildFrom
needs to receiveCC[A]
instead ofIterable[A]
. And that's all there is to it.And the result:
上面的问题是你的隐式转换
collectionExtras
导致获取的对象丢失类型信息。特别是,在上面的解决方案中,具体的集合类型丢失了,因为您向它传递了 Iterable[A] 类型的对象 - 从这一点开始,编译器不再知道的真实类型xs
。尽管构建器工厂CanBuildFrom
以编程方式确保集合的动态类型是正确的(您确实获得了Vector
),但静态地,编译器只知道zipWith< /code> 返回一个
Iterable
的东西。为了解决这个问题,不要让隐式转换采用
Iterable[A]
,而是让它采用IterableLike[A, Repr]
。为什么?Iterable[A]
通常声明为:与
Iterable
的区别在于,这个IterableLike[A, Repr]
保留了具体的集合类型作为Repr
。大多数具体集合,除了混合Iterable[A]
之外,还混合了IterableLike[A, Repr]
特征,替换Repr
他们可以这样做,因为类型参数
Repr
被声明为协变。长话短说,使用
IterableLike
会导致隐式转换以保留具体的集合类型信息(即Repr
),并在定义zipWith
时使用它> - 请注意,构建器工厂CanBuildFrom
现在将包含Repr
而不是Iterable[A]
作为第一个类型参数,从而导致相应的隐式对象待解决:更仔细地阅读您的问题表述(“如何编写一个 zipWith 方法,该方法返回与传递给它的集合类型相同的集合?”),在我看来,您希望拥有与那些相同类型的集合传递给
zipWith
,而不是隐式转换,它与ys
类型相同。原因与之前相同,请参阅下面的解决方案:
结果:
The problem above is that your implicit conversion
collectionExtras
causes the obtained object to lose type information. In particular, in the solution above, the concrete collection type is lost because you're passing it an object of typeIterable[A]
- from this point on, the compiler no longer knows the real type ofxs
. Although the builder factoryCanBuildFrom
programatically ensures that the dynamic type of the collection is correct (you really get aVector
), statically, the compiler knows only thatzipWith
returns something that is anIterable
.To solve this problem, instead of having the implicit conversion take an
Iterable[A]
, let it take anIterableLike[A, Repr]
. Why?Iterable[A]
is usually declared as something like:The difference with
Iterable
is that thisIterableLike[A, Repr]
keeps the concrete collection type asRepr
. Most concrete collections, in addition to mixing inIterable[A]
, also mix in the traitIterableLike[A, Repr]
, replacing theRepr
with their concrete type, like below:They can do this because type parameter
Repr
is declared as covariant.Long story short, using
IterableLike
causes you implicit conversion to keep the concrete collection type information (that isRepr
) around and use it when you definezipWith
- note that the builder factoryCanBuildFrom
will now containRepr
instead ofIterable[A]
for the first type parameter, causing the appropriate implicit object to be resolved:Reading your question formulation more carefully ("How to write a zipWith method that returns the same type of collection as those passed to it?"), it seems to me that you want to have the same type of collection as those passed to
zipWith
, not to the implicit conversion, that is the same type asys
.Same reasons as before, see solution below:
With results:
老实说,我不确定这到底是如何工作的:
我有点猴子修补 这个答案来自 Retronym 直到它起作用!
基本上,我想使用
CC[X]
类型构造函数来指示zipWith
应返回xs
的集合类型,但使用C
作为类型参数 (CC[C]
)。我想使用breakOut
来获得正确的结果类型。我有点希望作用域中有一个隐式的CanBuildFrom
,但随后收到了此错误消息:技巧是使用
Nothing
而不是Iterable[(A, B)]
。我猜隐式是在某处定义的...另外,我喜欢将您的 zipWith 视为 zip ,然后是地图,所以我更改了执行。这是您的实现:
注意本文提供了有关类型构造函数模式的一些背景知识。
To be honest I'm not sure how that really works:
I sort of monkey patched this answer from retronym until it worked!
Basically, I want to use the
CC[X]
type constructor to indicate thatzipWith
should return the collection type ofxs
but withC
as the type parameter (CC[C]
). And I want to usebreakOut
to get the right result type. I sort of hoped that there was aCanBuildFrom
implicit in scope but then got this error message:The trick was then to use
Nothing
instead ofIterable[(A, B)]
. I guess that implicit is defined somewhere...Also, I like to think of your
zipWith
aszip
and thenmap
, so I changed the implementation. Here is with your implementation:Note this article provides some background on the type constructor pattern.