2016-08-30 1 views
4

나는 M. Odersky에 의해 스칼라에서 프로그래밍을 읽고 있어요 그는왜 함수 꼬리가 재귀 적이 지 않습니까?

Functions like approximate, which call themselves as their last action, are called tail recursive.

그래서, 나는이 시도 말한다 :

object Main extends App { 
    implicit val mc = new MyClass(8) 
    val ti = new TestImplct 
    ti.test 
} 

class TestImplct { 
    def test(implicit mc : MyClass): Unit = { 
    println(mc.i) 
    mc.i -= 1 
    if(mc.i < 0){ 
     throw new IllegalArgumentException 
    } 
    test 
    } 
} 

class MyClass(var i : Int) 

IDEONE DEMO

을하지만, 다음과 같은 스택 추적을 생성

Exception in thread "main" java.lang.IllegalArgumentException 
    at TestImplct.test(Main.scala:13) 
    at TestImplct.test(Main.scala:15) 
    at TestImplct.test(Main.scala:15) 
    at TestImplct.test(Main.scala:15) 
    at TestImplct.test(Main.scala:15) 
    at TestImplct.test(Main.scala:15) 
    at TestImplct.test(Main.scala:15) 
    at TestImplct.test(Main.scala:15) 
    at TestImplct.test(Main.scala:15) 

즉, ge 각 재귀 호출에 대해 새 스택 프레임을 지정합니다. 하지만 마지막 행동은 스스로를 호출하는 것입니다. 무엇이 잘못되었으며 꼬리 재귀 적으로 만드는가?

컴파일러가 꼬리 - 호출 최적화를 수행하지 않는 이유는 무엇입니까?

+0

범위에 내재 된 MyClass 인스턴스가 있습니까? – Samar

+0

@Samar 예, 있습니다. 데모보기 – stella

+0

클래스를 정의했습니다. 하지만 당신이 그것을 어디에서 보았는지는 알 수 없습니다. – Samar

답변

9

@tailrec 주석으로 메소드를 표시해볼 수 있습니다. 당신이 그렇게한다면, 컴파일이 실패하고 컴파일러가 꼬리 재귀가이 최적화 할 수없는 이유를 보여줍니다 : 당신이 방법 final을 할 경우 예상대로 실제로

Main.scala:12: error: could not optimize @tailrec annotated method test: it is neither private nor final so can be overridden

는, 그것은 작동합니다.

+0

@ tailrec은 정말 멋진 것입니다. 고맙습니다! – stella

+1

흥미 롭습니다. 최종적으로이 방법으로 TCO를 허용하는 이유는 무엇입니까? – Samar

+0

@Samar + 1 또한 알고 싶습니다. – stella

2

코드가 올바르게 작동합니다. mc 개체의 값은 매 단계마다 감소합니다. 마지막 단계에서는 예외가 발생합니다.

당신은 예를 들어 Boolean가 될 함수의 리턴 타입을 변경하고, 그렇지 않으면 당신은 true를 반환하여 값이 < 0false을 반환 할 수 있습니다.

컴파일러가 재귀 함수 호출을 확인하려면 '@tailrec'주석을 사용하는 것이 좋습니다.

관련 문제