0

scala.collection.immutable.StreamScalazの (バージョン 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)

ここではandとMTransを含むモナド変換スタックです。EitherTStateTf: Int => MTrans[Int]

私は通常、要素をシーケンスすること (状態を渡すこと) だけに興味があり、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 コンビネータと Scalaz のフリー モナドfoldLeftからの入力に基づいて、 applicative を使用した次の単純な に基づくソリューションを思いつきました*>

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

これは (まだ) スタック セーフに見えますが、大量のヒープ スペースが必要であり、実行が非常に遅くなります。

4

0 に答える 0