102 votes

Retour en Scala

Je suis un programmeur débutant en Scala et je suis tombé sur un comportement bizarre.

def balanceMain(elem: List[Char]): Boolean =
  {
    if (elem.isEmpty)
      if (count == 0)
        true;
      else false;

    if (elem.head == '(')
      balanceMain(elem.tail, open, count + 1);....

Ci-dessus, en gros, je veux retourner vrai si elem.isEmpty y count == 0 . Sinon, je veux renvoyer false.

J'ai lu plus haut qu'il n'était pas nécessaire d'ajouter une déclaration de retour en Scala. J'ai donc omis return ci-dessus. Mais il ne renvoie pas le booléen. Si j'ajoute une instruction de retour comme return true . il fonctionne parfaitement. Pourquoi en est-il ainsi ?

De plus, pourquoi est-il considéré comme une mauvaise pratique d'avoir des déclarations de retour en Scala ?

159voto

dhg Points 26700

Ce n'est pas aussi simple que d'omettre l'élément return mot-clé. En Scala, s'il n'y a pas de return alors la dernière expression est considérée comme la valeur de retour. Ainsi, si la dernière expression est ce que vous voulez retourner, vous pouvez omettre l'élément return mot-clé. Mais si ce que vous voulez retourner est no la dernière expression, alors Scala ne saura pas que vous vouliez le rendre .

Un exemple :

def f() = {
  if (something)
    "A"
  else
    "B"
}

Ici, la dernière expression de la fonction f est une expression if/else qui donne lieu à une chaîne de caractères. Puisqu'il n'y a pas d'expression return marqué, Scala en déduira que vous vouliez retourner le résultat de cette expression if/else : une chaîne.

Maintenant, si nous ajoutons quelque chose après l'expression if/else :

def f() = {
  if (something)
    "A"
  else
    "B"

  if (somethingElse)
    1
  else
    2
}

Maintenant la dernière expression est une expression if/else qui évalue à un Int. Ainsi, le type de retour de f sera Int. Si nous voulions vraiment qu'il renvoie la chaîne de caractères, alors nous aurions des problèmes car Scala dispose de aucune idée que c'est ce que nous voulions. Nous devons donc corriger le problème, soit en stockant la chaîne dans une variable et en la renvoyant après la deuxième expression if/else, soit en modifiant l'ordre de manière à ce que la partie chaîne se produise en dernier.

Enfin, nous pouvons éviter le return même avec une expression if-else imbriquée comme la vôtre :

def f() = {
  if(somethingFirst) {
    if (something)      // Last expression of `if` returns a String
     "A"
    else
     "B"
  }
  else {
    if (somethingElse)
      1
    else
      2

    "C"                // Last expression of `else` returns a String
  }

}

27voto

Grmpfhmbl Points 897

Ce sujet est en fait un peu plus compliqué que ce qui est décrit dans les réponses données jusqu'à présent. Voici blogpost par Rob Norris l'explique plus en détail et donne des exemples de cas où l'utilisation de return peut réellement casser votre code (ou du moins avoir des effets non évidents).

À ce stade, permettez-moi de citer l'essentiel du message. La déclaration la plus importante se trouve au début. Imprimez-le comme un poster et mettez-le sur votre mur :-)

En return n'est pas "facultatif" ou "inféré" ; il modifie le sens de l'expression. sens de votre programme, et vous ne devriez jamais l'utiliser.

Il donne un exemple, où il casse réellement quelque chose, lorsque vous mettez une fonction en ligne.

// Inline add and addR
def sum(ns: Int*): Int = ns.foldLeft(0)((n, m) => n + m) // inlined add

scala> sum(33, 42, 99)
res2: Int = 174 // alright

def sumR(ns: Int*): Int = ns.foldLeft(0)((n, m) => return n + m) // inlined addR

scala> sumR(33, 42, 99)
res3: Int = 33 // um.

parce que

A return Lorsqu'elle est évaluée, l'expression abandonne le calcul en cours. et retourne à l'appelant de la méthode dans laquelle l'expression return apparaît.

Ce n'est qu'un des exemples donnés dans l'article en lien et c'est le plus facile à comprendre. Il y en a d'autres et je vous encourage vivement à y aller, à lire et à comprendre.

Lorsque vous venez de langages impératifs comme Java, cela peut sembler étrange au début, mais une fois que vous vous serez habitué à ce style, cela aura du sens. Permettez-moi de conclure par une autre citation :

Si vous vous trouvez dans une situation où vous pensez vouloir rentrer plus tôt, vous devez repenser la façon dont vous avez défini votre calcul.

5voto

jmdeldin Points 2932

Je ne programme pas Scala, mais j'utilise un autre langage avec des retours implicites (Ruby). Vous avez du code après votre if (elem.isEmpty) la dernière ligne de code est celle qui est renvoyée, c'est pourquoi vous n'obtenez pas ce que vous attendez.

EDIT : Voici une façon plus simple d'écrire votre fonction aussi. Utilisez simplement la valeur booléenne de isEmpty et count pour retourner automatiquement true ou false :

def balanceMain(elem: List[Char]): Boolean =
{
    elem.isEmpty && count == 0
}

5voto

Tharabas Points 2288

Par défaut, la dernière expression d'une fonction sera retournée. Dans votre exemple, il y a une autre expression après le point, où vous voulez votre valeur de retour. Si vous voulez renvoyer quelque chose avant la dernière expression, vous devez toujours utiliser return .

Vous pourriez modifier votre exemple comme suit, pour renvoyer un Boolean de la première partie

def balanceMain(elem: List[Char]): Boolean = {
  if (elem.isEmpty) {
    // == is a Boolean resulting function as well, so your can write it this way
    count == 0
  } else {
    // keep the rest in this block, the last value will be returned as well
    if (elem.head == "(") {
      balanceMain(elem.tail, open, count + 1)
    }
    // some more statements
    ...
    // just don't forget your Boolean in the end
    someBoolExpression
  }
}

4voto

Bart Schuller Points 1992

N'écrivez pas if sans une déclaration correspondante else . Une fois que vous avez ajouté le else à votre fragment, vous verrez que votre true y false sont en fait les dernières expressions de la fonction.

def balanceMain(elem: List[Char]): Boolean =
  {
    if (elem.isEmpty)
      if (count == 0)
        true
      else
        false
    else
      if (elem.head == '(')
        balanceMain(elem.tail, open, count + 1)
      else....

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