0

Mon cas d'utilisation est plus compliquée, mais est fondamentalement ci-dessous l'exemple simplifié de ce que je suis en train de réaliser (mon code d'origine est pour akka-stream):Turs récursion (@tailrec) fonction récursive vs erreur de débordement de la pile de la fonction scala non-récursif?

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() 
      } 
    } 
} 

Le problème est que la fonction de la pompe() peut entraîner un débordement de la pile (car la fonction de pompe est appelée encore et encore pour une condition spécifique).

Je peux écrire la fonction dans une version non récurrente comme les suivantes:

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() 
    } 
} 

Cependant, mon cas d'utilisation est beaucoup plus complexe; donc je voudrais vraiment utiliser la fonction récursive si c'est possible au lieu de convertir le code existant en while/for loop. Ma question est "dois-je faire" et y a-t-il une différence si je mets simplement l'annotation @tailrec sur le premier exemple pour que scala complier traite la pompe comme fonction de récursion de queue (@tailrec def pump(): Unité = {}). Dans mon cas, la fonction pump() doit être appelée séparément/après un intervalle fixe; pile-cadre n'est vraiment pas nécessaire ici.

Merci.

+4

"Y a-t-il une différence si je mets tout simplement l'annotation' @ tailrec' sur le premier exemple "- Non. L'annotation' @ tailrec' ne fait aucune différence. Les méthodes de queue-récursive sont toujours optimisées. (Plus précisément: les méthodes qui satisfont [les conditions énoncées dans la spécification] (http://scala-lang.org/files/archive/spec/2.13/06-expressions.html#function-applications).) La seule chose l'annotation '@ tailrec' génère une erreur de compilation si la méthode n'est pas récursive. [Cela ne change * pas * si la méthode est optimisée.] (Http://scala-lang.org/api/current/scala/annotation/tailrec.html) –

+0

Je conteste votre correspondance de motif sur 'null'. Au lieu de cela, j'intégrerais votre valeur "null" possible dans [Option # apply] (http://www.scala-lang.org/api/2.11.8/index.html#[email protected] [A] (x: A): Option [A]). Exemple: 'Option [String] {null}' === 'None' –

Répondre

1

Si vous mettez @tailrec et que le compilateur vous permet de l'exécuter, cela implique que votre code est éligible pour l'optimisation de la récursion de queue. En interne, le compilateur scala convertira le code récursif de la queue en une boucle. Je ne pense pas qu'une fonction récursive de queue provoquera une erreur de stackoverflow. Ainsi votre pompe() fonction ne provoquera pas stackoverflow si elle satisfait @tailrec.