我正在尝试scala.collection.immutable.Stream
使用 Scalaz'(版本 7.1.2)类型类遍历/排序大型流(例如)Traverse
,但我经常遇到java.lang.OutOfMemoryError: GC overhead limit exceeded
问题。
我的遍历大致如下所示:
import scalaz._
import Scalaz._
import scala.collection.immutable.Stream
Stream.range(1, 1000000000).traverse[MTrans, Int](f)
哪里MTrans
是一个单子变压器堆栈,包括EitherT
andStateT
和f: Int => MTrans[Int]
。
我通常只对元素排序(传递状态)感兴趣,只需要最终结果(MTrans[Int]
),而不是整个(物化)序列/流。
我有运行的版本,traverseKTrampoline
但这无济于事,因为这不是StackOverflowError
其他类似帖子中描述的问题。我也尝试了使用的组合,EphemeralStream
但sequence
没有成功。
我如何(内存)有效地遍历/排序这样的流?
更新 1
以下是我正在尝试做的更完整的示例。它与我所拥有的结构非常相似,并且表现出相同的问题(GC 开销限制在某些时候超过了)。
object Main {
import scalaz._
import Scalaz._
import Kleisli._
import scala.collection.immutable.Stream
import scala.language.higherKinds
case class IState(s: Int)
type IStateT[A] = StateT[Id, IState, A]
type MTransT[S[_], A] = EitherT[S, String, A]
type MTrans[A] = MTransT[IStateT, A]
def eval(k: Int): MTrans[Int] = {
for {
state <- get[IState].liftM[MTransT]
_ <- put(state.copy(s = state.s % k)).liftM[MTransT]
} yield (k + 1)
}
def process(i: Int, k: Int): MTrans[Int] = {
for {
state <- get[IState].liftM[MTransT]
_ <- put(state.copy(s = state.s + i)).liftM[MTransT]
res <- eval(k)
} yield res
}
def run() = {
val m = Stream
.range(1, 1000000000)
.traverseKTrampoline[MTrans, Int, Int](i => kleisli(process(i, _))).run(7)
m.run(IState(0))
}
}
更新 2
基于来自 Eric 和Applicative 与 monadic 组合器以及 Scalaz 中的免费 monad 的一些输入,我使用 applicative 提出了以下foldLeft
基于简单的解决方案*>
:
val m = Stream
.range(1, 1000000000)
.toEphemeralStream
.foldLeft(0.point[MTrans]) { acc => i =>
acc *> process(i, 3)
}
虽然这(仍然)似乎是堆栈安全的,但它需要大量的堆空间并且运行速度非常慢。