Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/scala/19.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Scala—在有序迭代器上延迟分组_Scala_Stream_Iterator_Lazy Evaluation - Fatal编程技术网

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) )
}