Scala—在有序迭代器上延迟分组
我有一个Scala—在有序迭代器上延迟分组,scala,stream,iterator,lazy-evaluation,Scala,Stream,Iterator,Lazy Evaluation,我有一个迭代器[Record],它是按Record.id的方式排序的: record.id=1 record.id=1 ... record.id=1 record.id=2 record.id=2 .. record.id=2 特定ID的记录可能会出现很多次,因此我想编写一个函数,将此迭代器作为输入,并以惰性方式返回一个iterator[iterator[Record]]输出 我能够得出以下结论,但在500K左右的记录后,它在StackOverflowerError上失败: def grou
迭代器[Record]
,它是按Record.id
的方式排序的:
record.id=1
record.id=1
...
record.id=1
record.id=2
record.id=2
..
record.id=2
特定ID的记录可能会出现很多次,因此我想编写一个函数,将此迭代器作为输入,并以惰性方式返回一个iterator[iterator[Record]]
输出
我能够得出以下结论,但在500K左右的记录后,它在StackOverflowerError
上失败:
def groupByIter[T, B](iterO: Iterator[T])(func: T => B): Iterator[Iterator[T]] = new Iterator[Iterator[T]] {
var iter = iterO
def hasNext = iter.hasNext
def next() = {
val first = iter.next()
val firstValue = func(first)
val (i1, i2) = iter.span(el => func(el) == firstValue)
iter = i2
Iterator(first) ++ i1
}
}
我做错了什么?这里的问题是,每个
迭代器.span
调用都会对尾随的迭代器进行另一个堆叠闭包,并且在没有任何蹦床的情况下很容易溢出
实际上,我不认为有一个实现,它不是记忆前缀迭代器的元素,因为后面的迭代器可以在前缀耗尽之前被访问
即使在中,也有一个队列
来记忆前导定义中的元素
我能想象的最简单的实现是通过Stream
实现的
implicit class StreamChopOps[T](xs: Stream[T]) {
def chopBy[U](f: T => U): Stream[Stream[T]] = xs match {
case x #:: _ =>
def eq(e: T) = f(e) == f(x)
xs.takeWhile(eq) #:: xs.dropWhile(eq).chopBy(f)
case _ => Stream.empty
}
}
虽然它可能不是最有表现力的,因为它会记忆很多。但通过适当的迭代,GC应该处理过多中间流的问题
您可以将其用作myIterator.toStream.chobby(f)
简单检查验证以下代码是否可以在没有SO的情况下运行
Iterator.fill(10000000)(Iterator(1,1,2)).flatten //1,1,2,1,1,2,...
.toStream.chopBy(identity) //(1,1),(2),(1,1),(2),...
.map(xs => xs.sum * xs.size).sum //60000000
受@Odomontois实现的肖比的启发,这里是我为迭代器实现的肖比。当然,每个块都应该适合分配的内存。它看起来不太优雅,但似乎很管用:)
这是一个用于匹配尾部的提取器:
object MatchTail {
def unapply[A] (l: Traversable[A]) = Some( (l.init, l.last) )
}
分组假设您迭代整个集合并对所有值进行分组。我认为懒惰是不可能的,因为有一个小的打字错误。你必须在chobby
中应用f
,比如xs.takeWhile(e=>f(e)==f(x))#:xs.dropWhile(e=>f(e)==f(x))。肖比(f)
@jeffreyveon肯定!谢谢,我尝试在调用链的末尾使用建议的方法与foreach,但内存溢出。看起来它首先迭代整个流,同时根据需要对其进行切分,并且只有在所有切分都就绪后,才能对每个切分调用foreach。因此,实际上,由于所有流都一次加载到内存中,因此流行为将丢失。@alexanderostrikov它肯定无法处理很长的相似元素序列,因为它在发送到下游之前等待每个元素结束。@Odomontois试图用可以发布到此处的简单代码重现问题,根据我目前的scala知识,我发现了一些我无法解释的东西。因此,当我这样称呼它时,实际上您的代码看起来像预期的那样工作(很抱歉第一条评论):Iterator.range(01000000000,1).toStream.chobby(x=>x%20==0).map(s=>s.toList.foreach(println)
但是当我这样称呼它时:val-schpped=Iterator.range(01000000000,1).toStream.chobby(x=>x%20==0).map(s=>s.toList)chopped.foreach(println)
I get-OutOfMemoryError
object MatchTail {
def unapply[A] (l: Traversable[A]) = Some( (l.init, l.last) )
}