6

我想使用 IO 单子。

但是此代码不适用于大文件。我收到了 StackOverflowError。我尝试了该-DXss选项,但它引发了同样的错误。

val main = for {
  l <- getFileLines(file)(collect[String, List]).map(_.run)
  _ <- l.traverse_(putStrLn)
} yield ()

我该怎么做?


我写了输出所有元素的Iteratee。

def putStrLn[E: Show]: IterV[E, IO[Unit]] = {
  import IterV._
  def step(i: IO[Unit])(input: Input[E]): IterV[E, IO[Unit]] =
    input(el = e => Cont(step(i >|> effects.putStrLn(e.shows))),
      empty = Cont(step(i)),
          eof = Done(i, EOF[E]))
  Cont(step(mzero[IO[Unit]]))
}
val main = for {
  i <- getFileLines(file)(putStrLn).map(_.run)
} yield i.unsafePerformIO

这也是同样的结果。

我认为是由 IO 实现引起的。

4

1 回答 1

4

这是因为scalacloop内部没有getReaderLines针对尾调用进行优化。loop是尾递归的,但我认为case匿名函数语法会妨碍。

编辑:实际上它甚至不是尾递归(在 IO monad 中的包装)在递归调用之后导致至少再调用一次。当我昨天进行测试时,我使用了类似的代码,但我放弃了 IO monad,然后可以使 Iteratee 尾递归。下面的文字,假设没有 IO 单子......

我昨天在尝试迭代器时碰巧发现了这一点。我认为将签名更改loop为此会有所帮助(因此暂时您可能需要重新实现getFilesLinesand getReaderLines

@annotations.tailrec
def loop(it: IterV[String, A]): IO[IterV[String, A]] = it match {
  // ...
}

我们可能应该将此报告给 scalaz 人(并且可能会为 scala 开一张增强票)。

这显示了会发生什么(代码模糊地类似于getReaderLines.loop):

@annotation.tailrec
def f(i: Int): Int = i match {
  case 0 => 0
  case x => f(x - 1)
}
// f: (i: Int)Int

@annotation.tailrec
def g: Int => Int = {
  case 0 => 0
  case x => g(x - 1)
}
/* error: could not optimize @tailrec annotated method g: 
it contains a recursive call not in tail position
       def g: Int => Int = {
                           ^
*/
于 2011-10-03T14:31:17.023 回答