2011-01-15 10 views
8

Definire un metodo iterativo su un oggetto:Metodo è coda ricorsiva quando definito nell'oggetto ma non sulla classe

object Recursive { 
    def recurse(maxDepth: Int = 10): Unit = { 
     if (maxDepth == 0) throw new Exception 
     recurse(maxDepth - 1) 
    } 
} 

dà:

scala> Recursive.recurse(10) 
java.lang.Exception 
     at Recursive$.recurse(<console>:7) 
     at .<init>(<console>:7) 
     at .<clinit>(<console>) 
     at RequestResult$.<init>(<console>:9) 
     at RequestResult$.<clinit>(<console>) 
     at RequestResult$scala_repl_result(<console>) 
     at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) 
     at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39) 
     at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25) 
     at java.lang.reflect.Method.invoke(Method.java:597) 
     at scala.tools.nsc.Interpreter$Request$$anonfun$loadAndRun$1$$anonfun$apply$17.apply(Interpreter.scala:988) 
     at scala.tools.nsc.Interpreter$Request$$anonfun$loadAndRun$1$$anonfun$apply$17.apply(Interpreter.scala:988) 
     at scala.util.control.Exception$Catch.apply(Exception.scal... 

Ma definente su una classe:

class Recursive { 
    def recurse(maxDepth: Int = 10): Unit = { 
     if (maxDepth == 0) throw new Exception 
     recurse(maxDepth - 1) 
    } 
} 

fornisce:

scala> new Recursive recurse(10) 
java.lang.Exception 
     at Recursive.recurse(<console>:7) 
     at Recursive.recurse(<console>:8) 
     at Recursive.recurse(<console>:8) 
     at Recursive.recurse(<console>:8) 
     at Recursive.recurse(<console>:8) 
     at Recursive.recurse(<console>:8) 
     at Recursive.recurse(<console>:8) 
     at Recursive.recurse(<console>:8) 
     at Recursive.recurse(<console>:8) 
     at Recursive.recurse(<console>:8) 
     at Recursive.recurse(<console>:8) 
     at .<init>(<console>:7) 
     at .<clinit>(<console>) 
     at RequestResult$.<init>(<console>:9) 
     at RequestResult$.<clinit>(<console>) 
     at RequestResult$scala_repl_result(<console>) 
     at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) 
     at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39) 
     at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAcc.. 

I metodi sono identici. Perché non è ricorsivo in coda se definito in una classe?

+1

utilizzare sempre @tailrec per verificare l'intenzione – ron

risposta

16

Se si desidera eseguire la ricorsione in coda, non è possibile sovrascrivere lo recurse. Se recurse è sovrascrivibile, come nella dichiarazione class, qualsiasi ricorsione al suo interno deve utilizzare l'invocazione del metodo dinamico (perché è potenzialmente polimorfica), che non può essere ottimizzata su un'istruzione goto-style.

La dichiarazione di singleton object assicura staticamente la chiamata non equivoca a recurse e consente al compilatore di procedere con l'ottimizzazione della ricorsione di coda.

+0

Ah! Quindi "l'ultimo def recurse ..." fa il trucco. –

10

Se si prevede che un metodo sia ricorsivo in coda, è necessario annotarlo con @tailrec. Se il compilatore non può applicare il TCO, si verificherà un errore.

scala> import annotation._       
import annotation._ 

scala> class Recursive {          
    |  @tailrec def recurse(maxDepth: Int = 10): Unit = { 
    |   if (maxDepth == 0) throw new Exception   
    |   recurse(maxDepth - 1)       
    |  }             
    | } 
<console>:12: error: could not optimize @tailrec annotated method: it is neither private nor final so can be overridden 
      @tailrec def recurse(maxDepth: Int = 10): Unit = { 
         ^
Problemi correlati