使用scalaz Traverse类型类的内存高效流遍历

时间:2015-05-22 21:28:09

标签: stream traversal scalaz scalaz7

我尝试使用Scalaz'来遍历/排序大型流(例如scala.collection.immutable.Stream)。 (版本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是包含EitherTStateT以及f: Int => MTrans[Int]的monad变换器堆栈。

我通常只对序列元素(传递状态)感兴趣,只需要最终结果(MTrans[Int]),而不是整个(物化)序列/流。

我的版本与traverseKTrampoline一起运行但是没有帮助,因为这不是其他类似帖子中描述的StackOverflowError问题。我还尝试过使用EphemeralStreamsequence的组合,但没有成功。

我如何(记忆)有效地遍历/排序这样的流?

更新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 vs. monadic combinators and the free monad in Scalaz的一些意见,我使用应用foldLeft提出了以下基于*>的简单解决方案:

val m = Stream
  .range(1, 1000000000)
  .toEphemeralStream
  .foldLeft(0.point[MTrans]) { acc => i =>
    acc *> process(i, 3)
}

虽然这(仍然)似乎是堆栈安全的,但它需要大量的堆空间并且运行速度非常慢。

0 个答案:

没有答案