无法构建集合

时间:2016-03-17 19:22:25

标签: scala collections

我有一个Stream数据,除了最终的 n 元素之外,我想扔掉所有内容。如果输入没有足够的元素,则生成的流将使用None填充。这就是我想出的:

def lastN[T](in: Stream[T], len: Int): Stream[Option[T]] =
  in.foldLeft(Vector.fill[Option[T]](len)(None))(_.tail :+ Option(_)).to[Stream]

我为内部缓冲区选择了Vector因为tailappend performance characteristics

一切正常。也许有更好的方法? [注意:总是更好的方式。]

但是假设Iterator是输入数据的更合适的表示?没问题,只需将Stream的3个提及替换为Iterator,一切正常。

好的,为什么不同时/两个?

我希望我能做到这样的事情:

import scala.language.higherKinds
def lastN[T, C[U] <: TraversableOnce[U] ](in: C[T], len: Int): C[Option[T]] =
  in.foldLeft(Vector.fill[Option[T]](len)(None))(_.tail :+ Option(_)).to[C] 

唉,不要去。

  

错误:无法构造类型为C [Option [T]]的集合   Option [T]类型的元素基于Nothing类型的集合。

我已尝试直接使用CanBuildFrom,但我只是没有提出神奇的公式。

2 个答案:

答案 0 :(得分:1)

我认为使用Queue作为内部缓冲区更为自然。它在语义上更适合这种处理,scala.collection.immutable.Queue用两个List实现,实际上可能比Vector更有效(你必须做出来)一个衡量标准,以确定当然是否属于这种情况)。否则API保持完全相同:您只需将Vector替换为Queue

对于CanBuildFrom,您在代码中使用它来调用to方法。您可以查阅完整的signature,了解您必须提出的CanBuildFrom内容:

def to[Col[_]](implicit cbf: CanBuildFrom[Nothing, A, Col[A]]): Col[A] 

所以,你需要CanBuildFrom[Nothing, Option[T], C[Option[T]]]

将所有可能的实现放在一起看起来像这样:

import scala.collection.generic.CanBuildFrom
import scala.collection.immutable.Queue

def lastN[T, C[U] <: TraversableOnce[U]](in: C[T], len: Int)(
  implicit cbf: CanBuildFrom[Nothing, Option[T], C[Option[T]]]
): C[Option[T]] =
  in.foldLeft(Queue.fill(len)(None: Option[T]))(_.tail :+ Option(_)).to[C]

至于你的评论,编译器知道要调用to它需要CanBuildFrom[Nothing, Option[T], C[Option[T]]],但它不能自动找到带抽象类型的隐式参数。

但是如果您在CanBuildFrom[Nothing, Option[T], C[Option[T]]]签名中添加了请求lastN,那么当您调用示例lastN(Vector(1,2,3), 2)时,编译器会知道CVectorTInt,因此必须传递CanBuildFrom[Nothing, Option[Int], Vector[Option[Int]]]

这里所有类型都是具体的,编译器可以使用通常的implicit lookup rules找到CanBuildFrom的相关实例。我相信,在这个例子中,它会在Vector的伴随对象中找到一个。

答案 1 :(得分:1)

如果您想获取Iterable的最后N个元素,可以使用takeRight函数。这适用于从Iterable继承的任何集合,因此它适用于Stream。不幸的是,有人指出这不适用于Iterator

def lastN[T](in: Iterable[T], n: Int) = in.takeRight(n)

TraversableIterator的超类,确实有一个toIterable函数可以使用。如果你真的想让它尽可能通用,你可以尝试:

def lastN[T](in: Traversable[T], n: Int) = in.toIterable.takeRight(n)