我有一个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
因为tail
和append
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
,但我只是没有提出神奇的公式。
答案 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)
时,编译器会知道C
是Vector
,T
为Int
,因此必须传递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)
Traversable
,Iterator
的超类,确实有一个toIterable
函数可以使用。如果你真的想让它尽可能通用,你可以尝试:
def lastN[T](in: Traversable[T], n: Int) = in.toIterable.takeRight(n)