Han*_*örr 20 functional-programming scala
是否可以将函数式编程应用于Scala流,以便顺序处理流,但流的已处理部分可以被垃圾收集?
例如,我定义了一个Stream
包含从数字start
到end
:
def fromToStream(start: Int, end: Int) : Stream[Int] = {
if (end < start) Stream.empty
else start #:: fromToStream(start+1, end)
}
Run Code Online (Sandbox Code Playgroud)
如果我以函数式总结值:
println(fromToStream(1,10000000).reduceLeft(_+_))
Run Code Online (Sandbox Code Playgroud)
我得到一个OutOfMemoryError
- 也许是因为调用的堆栈帧reduceLeft
保存了对流的头部的引用.但如果我以迭代方式执行此操作,它的工作原理如下:
var sum = 0
for (i <- fromToStream(1,10000000)) {
sum += i
}
Run Code Online (Sandbox Code Playgroud)
有没有办法以功能的方式做到这一点而没有得到OutOfMemory
?
huy*_*hjl 19
当我开始学习时,Stream
我认为这很酷.然后我意识到Iterator
我几乎一直都想要使用它.
如果你确实需要Stream
但想做reduceLeft
工作:
fromToStream(1,10000000).toIterator.reduceLeft(_ + _)
Run Code Online (Sandbox Code Playgroud)
如果您尝试上面的行,它将垃圾收集很好.我发现使用Stream非常棘手,因为它很容易保持头部而不会意识到它.有时标准的lib会以非常微妙的方式为你保留它.
axe*_*l22 13
是的你可以.诀窍是使用尾递归方法,以便本地堆栈帧包含对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
Run Code Online (Sandbox Code Playgroud)
此外,您必须确保传递给上述方法的last
内容在堆栈上只有一个引用.如果将a存储Stream
到局部变量或值中,则在调用last
方法时不会对其进行垃圾回收,因为它的参数不是唯一的参考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)
Run Code Online (Sandbox Code Playgroud)
总结一下:
Stream
编辑:
请注意,这也有效,并且不会导致内存不足错误:
scala> def s = Stream.range(0, 100000000)
s: scala.collection.immutable.Stream[Int]
scala> last(s)
res1: Int = 99999999
Run Code Online (Sandbox Code Playgroud)
EDIT2:
在reduceLeft
您需要的情况下,您必须为结果定义一个带有累加器参数的辅助方法.
对于reduceLeft,您需要一个accumulator参数,您可以使用默认参数将其设置为某个值.一个简化的例子:
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
Run Code Online (Sandbox Code Playgroud)