2011-06-09 90 views
4

...尽管它是尾巴呼叫优化?这是为什么抛出堆栈溢出异常?

def areStreamsEqual(stream1: InputStream, stream2: InputStream): Boolean = 
{ 
    val one = stream1.read() 
    val two = stream2.read() 
    if(one != two) 
     false 
    else if(one == -1 && two == -1) 
     true 
    else 
     areStreamsEqual(stream1, stream2) 
} 

有没有办法强制Scala编译器在这里做一个尾部调用优化?

+5

如果方法没有通过[@tailrec](http://www.scala-lang.org/api/current/scala/annotation/tailrec.html)进行TCO处理,您可以告诉scalac抛出错误,注解。 (虽然这个注释不会强制/使其TCO'd)。 – 2011-06-09 19:18:52

回答

6

感谢pst评论@tailrec。鉴于注解scala编译器错误消息解释了不优化该方法的原因。

<filename>.scala:64: error: could not optimize @tailrec annotated method: it is neither private nor final so can be overridden 
def areStreamsEqual(stream1: InputStream, stream2: InputStream): Boolean = 

制作方法私有排序出来

我怀疑是在字节码水平,也有方法调用两个指令:virtual_call和tail_call。

+3

关于你最后的评论,在字节码级不支持tail调用。实际上scalac会将递归方法重写为迭代方法。 – 2011-06-09 21:47:14

+3

在字节码级别有四个调用指令:'invokestatic','invokespecial','invokevirtual','invokeinterface'。第五个'invokedynamic'即将在Java 7中出现.Scala不会使用这些方法来进行尾递归方法。相反,它将呼叫转换为一个简单的“goto”,它跳转到方法的顶部。如果您将该方法编写为“while”循环而不是递归函数,则这与完成的字节码完全相同。 – 2011-06-10 01:22:44

+0

确实没有tail_call指令。我发现这个stackoverflow答案有用解释为什么编译器拒绝优化方法http://stackoverflow.com/questions/4785502/why-wont-the-scala-compiler-apply-tail-call-optimization-unless-a-方法-是-FINA – luntain 2011-06-10 13:35:02

1

对于任何试图重新在REPL编译器错误,你必须包装的方法,一类是这样的:

class Test { 
@annotation.tailrec def areStreamsEqual(stream1: InputStream, stream2: InputStream): Boolean = 
{ 
    val one = stream1.read() 
    val two = stream2.read() 
    if(one != two) 
     false 
    else if(one == -1 && two == -1) 
     true 
    else 
     areStreamsEqual(stream1, stream2) 
} 
} 

如果您只需将法进入REPL,这将是TCO” d很好,因为类之外的方法不能被覆盖。