2017-02-01 13 views
0

내 사용 사례가 더 복잡하지만, 기본적으로 아래 내가 달성하기 위해 노력하고있는 무슨의 간단한 예입니다 (내 원래 코드는 akka 스트림입니다) :꼬리 재귀 (@tailrec) 재귀 함수 대 비 재귀 함수 스칼라 스택 오버플로 오류?

offset := 0 

//pump data 
def pump(): Unit = { 
    elem := poller.getNumberFromOffset(offset) 

    elem match { 
     case null => doSomething() 
     case Completed => doSomethingElse() 
     case _ => 
      offset += 1 
      //check if the element is matched with a pre-supplied selector/filter function 
      if(filterFunc(elem)) { 
       doSomething2() 
      } else { 
       //if the element doesn't match; increase offset and try again; can sleep for a while here    
       pump() 
      } 
    } 
} 

문제는 그 펌프() 함수 수도있다 스택 오버 플로우가 발생합니다 (펌프 기능이 특정 조건에 대해 반복적으로 호출되기 때문에).

나는 다음과 같은 비 재귀 버전으로 기능을 쓸 수

:

offset := 0 

//pump data 
def pump(): Unit = { 
    elem := poller.getNumberFromOffset(offset) 
    while(elem != null && elem != Completed && !filterFunc(elem)) { 
     offset += 1 
     elem = poller.getNumberFromOffset(offset) 
    }  

    elem match { 
     case null => doSomething() 
     case Completed => doSomethingElse() 
     case _ =>   
      offset += 1 
      doSomething2() 
    } 
} 

그러나 내 사용 사례는 훨씬 더 복잡하다; 그래서 기존 코드를 while/for 루프로 변환하는 대신 가능한 경우 재귀 함수를 사용하고 싶습니다.

첫 번째 예제에 @tailrec 주석을 넣으면 scala 컴파일러가 펌프를 tail-recursion 함수 (@tailrec def pump())로 처리하기 때문에 제 질문에 "그렇게해야합니까?"라는 질문이 있습니다. 단위 = {}). 필자의 경우, pump() 함수는 고정 간격으로 분리하여 호출해야합니다. 그래서 스택 프레임은 실제로 여기서 필요하지 않습니다.

감사합니다.

+4

"첫 번째 예제에'@ tailrec' 주석을 단순히 붙이면 어떤 차이가 있습니다. '- @ tailrec' 주석은 아무런 차이가 없습니다. Tail-recursive 메서드는 항상 최적화되어 있습니다. (좀더 정확히 말하자면 : [spec에 제시된 조건을 만족시키는] 메소드 (http://scala-lang.org/files/archive/spec/2.13/06-expressions.html#function-applications).) 유일한 것은 메소드가 tail-recursive가 아닌 경우'@ tailrec' 주석은 컴파일러 오류를 생성합니다. [방법이 최적화되었는지 여부는 * 변경하지 않습니다.] –

+0

패턴 일치에 대해 'null'에 도전합니다. 대신, [Option # apply] (http://www.scala-lang.org/api/2.11.8/index.html#[email protected] [A]())에서 가능한 'null'값을 래핑합니다. x : A) : 옵션 [A]). 예 :'Option [String] {null}'==='None' –

답변

1

@tailrec을 입력하면 컴파일러에서 실행할 수 있으므로 코드 조각이 꼬리 재귀 최적화를받을 수 있음을 의미합니다. 내부 스칼라 컴파일러는 꼬리 재귀 코드를 루프로 변환합니다. 나는 꼬리 재귀 함수가 @tailrec을 만족한다면 stackoverflow error.So 당신의 pump() 함수가 stackoverflow를 일으키지 않을 것이라고 생각하지 않는다.