我想写一个public IEnumerator<T> GetEnumerator() {
return GetRealEnumerator(_version);
}
private IEnumerator<T> GetRealEnumerator(int baseVersion) {
方法,它接受两个迭代并将它们合并在一起。 (也许合并不是描述我想要的最好的词,但是为了这个问题它是无关紧要的)。我希望这种方法是通用的,可以处理不同的具体迭代。
例如,merge
应返回merge(Set(1,2), Set(2,3))
和
Set(1,2,3)
应该返回merge(List(1,2), List(2,3))
。我做了以下天真的尝试,但编译器抱怨List(1, 2, 2, 3)
的类型:它是res
而不是Iterable[Any]
。
A
如何修复此编译错误? (我更感兴趣的是理解如何实现这样的功能,而不是为我做这个功能的库,因此非常感谢我解释为什么我的代码不起作用。)
答案 0 :(得分:4)
让我们开始说明您的代码无法正常工作的原因。首先,您不小心使用existential type的缩写语法,而不是实际使用更高级别的类型绑定的类型。
// What you wrote is equivalent to this
def merge[A <: Iterable[T] forSome {type T}](first: A, second: A): A
即使修复它也不能让你得到你想要的东西。
def merge[A, S[T] <: Iterable[T]](first: S[A], second: S[A]): S[A] = {
first ++ second // CanBuildFrom errors :(
}
这是因为++
没有使用类型边界来实现其多态性,它使用隐式CanBuildFrom[From, Elem, To]
。 CanBuildFrom
负责提供适当的Builder[Elem, To]
,这是一个可变缓冲区,我们用它来构建我们所需类型的集合。
这意味着我们必须向CanBuildFrom
提供它所希望的一切,并且一切都能正常运作?
import collection.generic.CanBuildFrom
// Cannot construct a collection of type S[A] with elements of type A
// based on a collection of type Iterable[A]
merge0[A, S[T] <: Iterable[T], That](x: S[A], y: S[A])
(implicit bf: CanBuildFrom[S[A], A, S[A]]): S[A] = x.++[A, S[A]](y)
不:(。
我已将额外的类型注释添加到++
,以使编译器错误更具相关性。这告诉我们的是,因为我们没有专门用我们自己的Iterable
覆盖++
S
,我们正在使用{ {1}}它的实现,恰好采用了从Iterable
构建到CanBuildFrom
的隐式Iterable
。
顺便提一下@ChrisMartin遇到的问题(这件事对他的答案来说真是一个冗长的评论)。
不幸的是,Scala没有提供这样的S
,所以看起来我们必须手动使用CanBuildFrom
。
我们走下兔洞......
让我们首先注意到CanBuildFrom
实际上是++
中最初实际定义的,因此我们可以使我们的自定义TraversableLike
更加通用。
merge
现在让我们实际实现该签名。
def merge[A, S[T] <: TraversableLike[T, S[T]], That](it: S[A], that: TraversableOnce[A])
(implicit bf: CanBuildFrom[S[A], A, That]): That = ???
请注意,我已将 import collection.mutable.Builder
def merge[A, S[T] <: TraversableLike[T, S[T]], That](it: S[A], that: TraversableOnce[A])
(implicit bf: CanBuildFrom[S[A], A, That]): That= {
// Getting our mutable buffer from CanBuildFrom
val builder: Builder[A, That] = bf()
builder ++= it
builder ++= that
builder.result()
}
*更改为GenTraversableOnce[B]
**。这是因为TraversableOnce[B]
Builder
工作的唯一方法是进行顺序访问***。这就是++=
的全部内容。它为您提供了一个可变缓冲区,您可以使用所需的所有值填充该缓冲区,然后将缓冲区转换为CanBuildFrom
所需的输出集合。
result
简而言之,scala> merge(List(1, 2, 3), List(2, 3, 4))
res0: List[Int] = List(1, 2, 3, 2, 3, 4)
scala> merge(Set(1, 2, 3), Set(2, 3, 4))
res1: scala.collection.immutable.Set[Int] = Set(1, 2, 3, 4)
scala> merge(List(1, 2, 3), Set(1, 2, 3))
res2: List[Int] = List(1, 2, 3, 1, 2, 3)
scala> merge(Set(1, 2, 3), List(1, 2, 3)) // Not the same behavior :(
res3: scala.collection.immutable.Set[Int] = Set(1, 2, 3)
机制允许您构建代码来处理我们经常希望在Scala集合的继承图的不同分支之间自动转换的事实,但它需要付出代价一些复杂性和偶尔不直观的行为。相应地权衡权衡。
<强>脚注强>:
*&#34;广义&#34;我们可以追踪的集合&#34; Traverse&#34;至少&#34;一次&#34;,但可能不是更多,以某种顺序,可能是顺序,也可能不顺序,例如也许是平行的。
**与CanBuildFrom
相同,但不是&#34; General&#34;因为它保证了顺序访问。
*** GenTraversableOnce
通过在TraversableLike
内部强行拨打seq
来解决这个问题,但我觉得,当他们可能有其他情况时,会欺骗别人并行期待它。强制呼叫者决定是否要放弃并行性;不要为他们无形地做。
答案 1 :(得分:0)
初步,以下是此答案中所有代码所需的导入:
import collection.GenTraversableOnce
import collection.generic.CanBuildFrom
首先查看the API doc以查看Iterable.++
的方法签名(请注意大多数馆藏are wrong的API文档,您需要点击“完整签名”才能看到真实的类型):
def ++[B >: A, That](that: GenTraversableOnce[B])
(implicit bf: CanBuildFrom[Iterable[A], B, That]): That
从那里你可以直接从实例方法转换为函数:
def merge[A, B >: A, That](it: Iterable[A], that: GenTraversableOnce[B])
(implicit bf: CanBuildFrom[Iterable[A], B, That]): That = it ++ that
打破这个局面:
[A, B >: A, That]
-
Iterable
有一个类型参数A
,而++
有两个类型参数B
和That
,因此生成的函数包含所有三个类型参数{{1} },A
和B
That
- 该方法属于it: Iterable[A]
,因此我们将其作为第一个值参数Iterable[A]
- 直接从that: GenTraversableOnce[B])(implicit bf: CanBuildFrom[Iterable[A], B, That]): That