2 votes

Comment indiquer au compilateur Scala qu'une boucle while retournera une valeur ?

Certains algorithmes exécutent une boucle while avec la condition true et se termineront (à coup sûr) à un moment donné par une déclaration de retour dans le corps de la boucle while. Par exemple :

def foo: Int = {
  while(true) {
    // At some time, the while loop will do a return statement inside its body
    if( ... )
      return 0
  }
}

Exemple simple (sans sens sémantique) :

def foo: Int = {
  var i = 0
  while(true) {
    i += 1
    if(i == 10)
      return 0
  }
}

Le compilateur Scala se plaint d'une incompatibilité de type, car la boucle while a le type Unit et le compilateur ne sait pas que la boucle while retournera une valeur à un moment donné. Nous pourrions résoudre ce problème avec une solution de contournement comme celle-ci :

def foo: Int = {
  var i = 0
  while(true) {
    i += 1
    if(i == 10)
      return 0
  }
  0 // !
}

Mais ça, c'est moche. Existe-t-il une meilleure solution ? Ou même une meilleure solution pour ce type de problème ?

14voto

jqno Points 7831

Vous pourriez lancer une exception :

def foo: Int = {
  var i = 0
  while(true) {
    i += 1
    if(i == 10)
      return 0
  }
  throw new IllegalStateException("This should never happen")
}

Le compilateur cessera de se plaindre de l'incompatibilité de type et, comme la boucle while renvoie toujours quelque chose, l'exception ne sera jamais levée. Et si elle l'est, vous trouverez rapidement où vous avez fait quelque chose de mal :).

Il existe d'autres façons d'écrire cette boucle qui sont plus idiomatiques et Scala-esque, mais étant donné le code que vous avez fourni, ceci fera le travail d'une manière claire et simple.

8voto

DaoWen Points 14480

Peut-être que vous devriez juste utiliser la récursion de queue à la place. Cela devrait finir par se compiler en un bytecode très similaire :

import scala.annotation.tailrec

def foo: Int = {
  @tailrec def bar(i: Int): Int = {
    val j = i + 1
    if (j == 10) return 0
    else bar(j)
  }
  bar(0)
}

Vous pouvez même utiliser la prise en charge de la valeur par défaut des paramètres :

@tailrec def foo(i: Int = 0): Int = {
  val j = i + 1
  if (j == 10) return 0
  else foo(j)
}

Notez que cette méthode vous oblige à appeler la fonction en tant que foo() no foo puisqu'il a un argument.

3voto

Jan Points 904

Une manière plus idiomatique serait d'utiliser la récursion. Quelque chose comme ça :

  def foo: Int = { 
    import scala.annotation.tailrec
    @tailrec def whileUnderTen(i: Int):Int = if ( i < 10) whileUnderTen(i+1) else 0
    whileUnderTen(0)
  }

1voto

Dave Griffith Points 12923

Pour ces occasions, j'ai défini une construction "forever" dans ma bibliothèque standard personnelle.

forever{
}

est en tout point équivalent à

while(true){
}

sauf que forever a un type Nothing tandis que l'équivalent while La construction a le type Unit . C'est l'une de ces petites possibilités d'extension qui font de Scala un tel plaisir.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X