Pourquoi Scala est-il revenu mais ne s'est-il pas cassé et a continué

22

Scala n'a pas breakou continue, donc certains comportements de boucle demandent un peu plus de réflexion.

La fin précoce d' une boucle nécessite une récursivité de queue, des exceptions ou scala.util.control.Breaks(qui utilise des exceptions).

La raison en est que, comme goto, ce sont des constructions de flux qui obscurcissent le flux, et peuvent être accomplies de manière meilleure et moins surprenante.

Mais il semble que ces mêmes arguments pourraient être utilisés return.

Pourquoi Scala a-t-il délibérément omis breaket continue, mais pas return?

Paul Draper
la source
1
Je peux imaginer que les auteurs du langage considèrent la récursivité de la queue comme le moyen de construire l'itération. Je peux imaginer cela breaket j'ai continuebesoin de machines de nettoyage supplémentaires. OTOH returnest un moyen de terminer une fonction de manière ordonnée, et toute machine de nettoyage est déjà là de toute façon.
9000
1
Il n'y a breakable { for { break; } }qu'une réflexion après coup, et probablement loin d'être efficace.
Joop Eggen
Parce qu'avec les fonctions, il n'y a en fait aucune raison. C'est la même chose en python. Chaque fois que vous utilisez une boucle for avec break, vous pouvez à la place écrire une fonction, mettre votre boucle dans la fonction et utiliser return. Je ne peux pas penser à une situation où ce n'est pas une bonne idée concernant le code propre. Pour les performances, un nettoyage peut être meilleur, mais les performances n'ont pas la priorité la plus élevée dans scala.
valenterry
2
Cette question semble avoir une réponse ici: stackoverflow.com/questions/3770989/…
Michael Shaw
3
@PaulDraper: La réponse breaket continueest contenue dans votre question et dans le lien dans votre question. La question de savoir returnexactement sur quoi portait la question que j'ai liée et à laquelle on a répondu, du moins dans la réponse acceptée la plus votée. Si les deux réponses réunies ne répondent pas à votre question, vous pouvez peut-être modifier la question pour la clarifier.
Michael Shaw

Réponses:

16

Break and Continue:

Dans une conférence sur Scala , Martin Odersky a donné 3 raisons de ne pas inclure la pause ou de continuer sur la diapositive 22:

  • Ils sont un peu impératifs; mieux utiliser de nombreuses fonctions plus petites.
  • Indique comment interagir avec les fermetures.
  • Ils ne sont pas nécessaires!

Et il dit ensuite: "Nous pouvons les prendre en charge uniquement dans les bibliothèques." Sur la diapositive 23, il donne le code qui implémente break. Bien que je ne connaisse pas assez bien Scala pour en être certain, il semble que l'extrait court sur cette diapositive soit tout ce qu'il faut pour implémenter break, et cela continuepourrait être implémenté dans un code qui est tout aussi court.

Être capable d'implémenter des trucs comme ça dans les bibliothèques simplifie le langage principal.

Dans 'Programming in Scala, Second Edition', de Martin Odersky, Lex Spoon et Bill Venners, l'explication suivante est donnée:

Vous avez peut-être remarqué qu'il n'y a eu aucune mention de breakou continue. Scala laisse de côté ces commandes car elles ne correspondent pas bien aux littéraux de fonction ... Il est clair ce que continuesignifie à l'intérieur d'une whileboucle, mais qu'est-ce que cela signifierait à l'intérieur d'un littéral de fonction? ... Il existe de nombreuses façons de programmer sans breaket continue, et si vous profitez des littéraux de fonction, ces alternatives peuvent souvent être plus courtes que le code d'origine.

Revenir:

Les retours peuvent être considérés comme un peu impératifs dans le style, car return est un verbe, une commande pour faire quelque chose. Mais ils peuvent également être vus d'une manière purement fonctionnelle / déclarative: ils définissent quelle est la valeur de retour de la fonction (même si, dans une fonction à plusieurs retours, ils ne donnent chacun qu'une définition partielle).

Dans le même livre, ils disent ce qui suit à propos de return:

En l'absence de toute returndéclaration explicite , une méthode Scala renvoie la dernière valeur calculée par la méthode. Le style recommandé pour les méthodes est en fait d'éviter d'avoir des returninstructions explicites et surtout multiples . Considérez plutôt chaque méthode comme une expression qui produit une valeur, qui est renvoyée.

Les méthodes terminent et renvoient une valeur, même si une returninstruction n'est pas utilisée, donc il ne peut y avoir de problème avec les fermetures, car sinon les fermetures ne fonctionneraient pas.

Il ne peut pas non plus y avoir de problème de bon maillage avec les littéraux de fonction, car la fonction doit quand même retourner une valeur.

Michael Shaw
la source
2
En ce qui concerne le retour, il semble y avoir de légers dangers: tpolecat.github.io/2014/05/09/return.html
bbarker
0

Je pense que les réponses précédentes rendent justice aux problèmes de définition de la sémantique pour breakou continuede manière linguistique pour Scala, avec des contextes relativement sans contraintes.

J'ai écrit une petite bibliothèque qui définit breaket continuedans un contexte plus contraint: itération sur des séquences via Scala for-comprehensions. En me concentrant sur ce contexte, je crois que la sémantique devient sans ambiguïté et facile à raisonner.

La bibliothèque est disponible ici: https://github.com/erikerlandson/breakable

Voici un exemple simple de ce à quoi il ressemble dans le code:

scala> import com.manyangled.breakable._
import com.manyangled.breakable._

scala> val bkb2 = for {
     |   (x, xLab) <- Stream.from(0).breakable   // create breakable sequence with a method
     |   (y, yLab) <- breakable(Stream.from(0))  // create with a function
     |   if (x % 2 == 1) continue(xLab)          // continue to next in outer "x" loop
     |   if (y % 2 == 0) continue(yLab)          // continue to next in inner "y" loop
     |   if (x > 10) break(xLab)                 // break the outer "x" loop
     |   if (y > x) break(yLab)                  // break the inner "y" loop
     | } yield (x, y)
bkb2: com.manyangled.breakable.Breakable[(Int, Int)] = com.manyangled.breakable.Breakable@34dc53d2

scala> bkb2.toVector
res0: Vector[(Int, Int)] = Vector((2,1), (4,1), (4,3), (6,1), (6,3), (6,5), (8,1), (8,3), (8,5), (8,7), (10,1), (10,3), (10,5), (10,7), (10,9))
eje
la source