Scala流的功能处理,没有OutOfMemory错误
可以将功能性编程应用于Scala流,以便顺序处理流,但已经处理的流的部分可以被垃圾收集?
例如,我定义一个包含从头到尾的数字的流: def fromToStream(start: Int,end: Int) : Stream[Int] = { if (end < start) Stream.empty else start #:: fromToStream(start+1,end) } 如果我总结一个功能风格的价值观: println(fromToStream(1,10000000).reduceLeft(_+_)) 我得到一个OutOfMemoryError – 可能是因为对reduceLeft的调用的堆栈框架持有对流头的引用。但是如果我以迭代的方式做到这一点,它的作用是: var sum = 0 for (i <- fromToStream(1,10000000)) { sum += i } 有没有办法做到这一点功能的风格没有得到一个OutOfMemory? 更新:这是现在修复的a bug in scala。所以这或多或少已经过时了。 解决方法
是的你可以。诀窍是使用尾递归方法,以便本地堆栈帧包含对Stream实例的唯一引用。由于该方法是尾递归的,所以当前一个Stream头的本地引用一经递归调用自身就会被清除,从而使GC可以随时收集Stream的开始。
Welcome to Scala version 2.9.0.r23459-b20101108091606 (Java HotSpot(TM) Server VM,Java 1.6.0_20). Type in expressions to have them evaluated. Type :help for more information. scala> import collection.immutable.Stream import collection.immutable.Stream scala> import annotation.tailrec import annotation.tailrec scala> @tailrec def last(s: Stream[Int]): Int = if (s.tail.isEmpty) s.head else last(s.tail) last: (s: scala.collection.immutable.Stream[Int])Int scala> last(Stream.range(0,100000000)) res2: Int = 99999999 此外,您必须确保传递给上述方法的东西在堆栈中只有一个引用。如果将Stream存储到本地变量或值中,则在调用最后一个方法时不会垃圾回收,因为它的参数不是Stream剩下的唯一参考。下面的代码耗尽内存。 scala> val s = Stream.range(0,100000000) s: scala.collection.immutable.Stream[Int] = Stream(0,?) scala> last(s) Exception in thread "main" java.lang.OutOfMemoryError: Java heap space at sun.net.www.ParseUtil.encodePath(ParseUtil.java:84) at sun.misc.URLClassPath$JarLoader.checkResource(URLClassPath.java:674) at sun.misc.URLClassPath$JarLoader.getResource(URLClassPath.java:759) at sun.misc.URLClassPath.getResource(URLClassPath.java:169) at java.net.URLClassLoader$1.run(URLClassLoader.java:194) at java.security.AccessController.doPrivileged(Native Method) at java.net.URLClassLoader.findClass(URLClassLoader.java:190) at java.lang.ClassLoader.loadClass(ClassLoader.java:307) at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:301) at java.lang.ClassLoader.loadClass(ClassLoader.java:248) at scala.tools.nsc.Interpreter$Request$$anonfun$onErr$1$1.apply(Interpreter.scala:978) at scala.tools.nsc.Interpreter$Request$$anonfun$onErr$1$1.apply(Interpreter.scala:976) at scala.util.control.Exception$Catch.apply(Exception.scala:80) at scala.tools.nsc.Interpreter$Request.loadAndRun(Interpreter.scala:984) at scala.tools.nsc.Interpreter.loadAndRunReq$1(Interpreter.scala:579) at scala.tools.nsc.Interpreter.interpret(Interpreter.scala:599) at scala.tools.nsc.Interpreter.interpret(Interpreter.scala:576) at scala.tools.nsc.InterpreterLoop.reallyInterpret$1(InterpreterLoop.scala:472) at scala.tools.nsc.InterpreterLoop.interpretStartingWith(InterpreterLoop.scala:515) at scala.tools.nsc.InterpreterLoop.command(InterpreterLoop.scala:362) at scala.tools.nsc.InterpreterLoop.processLine$1(InterpreterLoop.scala:243) at scala.tools.nsc.InterpreterLoop.repl(InterpreterLoop.scala:249) at scala.tools.nsc.InterpreterLoop.main(InterpreterLoop.scala:559) at scala.tools.nsc.MainGenericRunner$.process(MainGenericRunner.scala:75) at scala.tools.nsc.MainGenericRunner$.main(MainGenericRunner.scala:31) at scala.tools.nsc.MainGenericRunner.main(MainGenericRunner.scala) 总结: >使用尾递归方法 编辑: 请注意,这也是有效的,不会导致内存不足错误: scala> def s = Stream.range(0,100000000) s: scala.collection.immutable.Stream[Int] scala> last(s) res1: Int = 99999999 EDIT2: 在您需要的reduceLeft的情况下,您必须使用累加器参数为结果定义一个帮助方法。 对于reduceLeft,您需要一个累加器参数,您可以使用默认参数将其设置为某个值。一个简化的例子: scala> @tailrec def rcl(s: Stream[Int],acc: Int = 0): Int = if (s.isEmpty) acc else rcl(s.tail,acc + s.head) rcl: (s: scala.collection.immutable.Stream[Int],acc: Int)Int scala> rcl(Stream.range(0,10000000)) res6: Int = -2014260032 (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |