복붙노트

[SCALA] 스칼라의 기능 처리에 OutOfMemory 오류없이 스트림

SCALA

스칼라의 기능 처리에 OutOfMemory 오류없이 스트림

이 스칼라 함수 프로그래밍을 적용하는 것이 가능하다 스트림이 순차적으로 처리되고 있지만, 스트림의 일부가 이미 처리 된 가비지 수집 될 수 있도록 스트림?

예를 들어, 나는 처음부터 끝까지 숫자를 포함하는 스트림을 정의합니다 :

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에 대한 호출의 StackFrame을 스트림의 머리에 대한 참조를 보유 아마도 때문이다. 나는 반복 스타일이 작업을 수행 할 경우, 그것은 작동합니다 :

var sum = 0
for (i <- fromToStream(1,10000000)) {
  sum += i
}

에서 OutOfMemory을받지 않고 기능적인 스타일이 할 수있는 방법이 있나요?

UPDATE :이 수정되었습니다 스칼라의 버그했다. 이 이제 유효 기간이 더 많거나 적은 그래서.

해결법

  1. ==============================

    1.그래 넌 할수있어. 트릭 로컬 스택 프레임 스트림 인스턴스 만 참조가 포함되도록 사용 꼬리 재귀 방법이다. 이 방법은 꼬리 재귀이기 때문에 재귀 따라서 당신이가는대로 스트림의 시작을 수집하는 GC 수 있도록, 자신을 호출하면, 이전의 스트림 헤드에 로컬 참조가 삭제됩니다.

    그래 넌 할수있어. 트릭 로컬 스택 프레임 스트림 인스턴스 만 참조가 포함되도록 사용 꼬리 재귀 방법이다. 이 방법은 꼬리 재귀이기 때문에 재귀 따라서 당신이가는대로 스트림의 시작을 수집하는 GC 수 있도록, 자신을 호출하면, 이전의 스트림 헤드에 로컬 참조가 삭제됩니다.

    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
    

    또한, 당신은 당신이 마지막으로 위의 방법으로 전달하는 것은 스택에 하나의 참조가 있는지 확인해야합니다. 로컬 변수 나 값으로 스트림을 저장하는 경우 마지막 메서드를 호출 할 때 인수가 스트림에 남아있는 유일한 참조하지 않기 때문에, 그것은 쓰레기는 수집되지 않습니다. 아래 코드는 메모리가 실행됩니다.

    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
    

    Aaditi :

    그리고 당신이 필요로하는 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
    
  2. ==============================

    2.내가 스트림 I에 대한 학습을 ​​시작했을 때이 멋진 생각했다. 그럼 반복자 내가 거의 모든 시간을 사용하고자하는 것입니다 깨달았다.

    내가 스트림 I에 대한 학습을 ​​시작했을 때이 멋진 생각했다. 그럼 반복자 내가 거의 모든 시간을 사용하고자하는 것입니다 깨달았다.

    경우 당신이 필요 스트림을하지만 reduceLeft 작동하게하려면 :

    fromToStream(1,10000000).toIterator.reduceLeft(_ + _)
    

    위의 라인을 시도 할 경우, 쓰레기 수집 잘 것이다. 나는 그것을 실현하지 않고 머리에 개최 쉽게로 스트림을 사용하여 까다로운 것으로 나타났습니다. 매우 미묘한 방식으로 - 때로는 표준 LIB는 당신을 위해 그것을에 개최한다.

  3. ==============================

    3.당신은 Scalaz의 임시 스트림을보고 할 수 있습니다.

    당신은 Scalaz의 임시 스트림을보고 할 수 있습니다.

  4. ==============================

    4.그것이 나오는 것에 따라,이 reduceLeft의 현재 구현의 버그입니다. 문제는 reduceLeft가 foldLeft를 호출, 따라서 reduceLeft의 StackFrame을 전체 통화 중에 스트림의 선두에 대한 참조를 보유하고 있다는 점이다. foldLeft이 문제를 피하기 위해 꼬리 재귀를 사용합니다. 비교:

    그것이 나오는 것에 따라,이 reduceLeft의 현재 구현의 버그입니다. 문제는 reduceLeft가 foldLeft를 호출, 따라서 reduceLeft의 StackFrame을 전체 통화 중에 스트림의 선두에 대한 참조를 보유하고 있다는 점이다. foldLeft이 문제를 피하기 위해 꼬리 재귀를 사용합니다. 비교:

    (1 to 10000000).toStream.foldLeft(0)(_+_)
    (1 to 10000000).toStream.reduceLeft(_+_)
    

    이러한 의미 적으로 동일합니다. 스칼라 버전 2.8.0에서 foldLeft에 대한 호출이 작동하지만, reduceLeft에 대한 호출은에 OutOfMemory가 발생합니다. reduceLeft가 자신의 작업을 수행 할 경우이 문제가 발생하지 않을 것입니다.

  5. from https://stackoverflow.com/questions/4132924/functional-processing-of-scala-streams-without-outofmemory-errors by cc-by-sa and MIT license