web-dev-qa-db-fra.com

Pourquoi Scala a-t-il un retour mais ne se casse pas et continue

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

Fin d'une boucle tôt 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 pour return.

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

22
Paul Draper

Break and Continue:

Dans n discours sur Scala , Martin Odersky a donné 3 raisons de ne pas inclure de 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 du code qui implémente break. Bien que je ne sache pas assez Scala assez bien pour être certain, il semble que le court extrait de cette diapositive soit tout ce qui est nécessaire pour implémenter break, et que continue pourrait être implémenté dans un code 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', par 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 break ou continue. Scala omet ces commandes car elles ne correspondent pas bien aux littéraux de fonction ... Il est clair ce que continue signifie à l'intérieur d'une boucle while, mais que cela signifie à l'intérieur d'un littéral de fonction? ... Il existe de nombreuses façons de programmer sans break et continue, et si vous profitez des littéraux de fonction, ces alternatives peuvent souvent être plus courtes que le code d'origine .

Retour:

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 aussi être vus de 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 instruction return 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 explicite, et surtout plusieurs instructions return. Considérez plutôt chaque méthode comme une expression qui renvoie une valeur, qui est renvoyée.

Les méthodes terminent et renvoient une valeur, même si une instruction return 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.

16
Michael Shaw

Je pense que les réponses précédentes rendent justice aux problèmes de définition de la sémantique pour break ou continue d'une manière à l'échelle du langage pour Scala, avec des contextes relativement sans contraintes.

J'ai écrit une petite bibliothèque qui définit break et continue dans un contexte plus contraint: itération sur des séquences via Scala for-comprehensions En me concentrant sur ce contexte, je pense 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))
0
eje