Comment puis-je sortir d'une boucle dans Scala?

Comment puis-je sortir une boucle?

var largest=0
for(i<-999 to 1 by -1) {
    for (j<-i to 1 by -1) {
        val product=i*j
        if (largest>product)
            // I want to break out here
        else
           if(product.toString.equals(product.toString.reverse))
              largest=largest max product
    }
}

Comment transformer les boucles imbriquées en récursion de queue?

De Scala Talk au FOSDEM 2009 http://www.slideshare.net/Odersky/fosdem-2009-1013261 sur la 22e page:

Pause et continuer Scala ne les a pas. Pourquoi? Ils sont un peu impératif; mieux utiliser de nombreuses fonctions plus petites Problème comment interagir avec les fermetures. Ils ne sont pas nécessaires!

Quelle est l'explication?

242
demandé sur bjb568 2010-04-30 10:34:08

17 réponses

Vous avez trois (ou plus) options pour sortir des boucles.

Supposons que vous voulez additionner des nombres jusqu'à ce que le total soit supérieur à 1000. Vous essayez

var sum = 0
for (i <- 0 to 1000) sum += i

, Sauf que vous voulez arrêter quand (somme > 1000).

Que faire? Il y a plusieurs options.

(1a) utilisez une construction qui inclut un conditionnel que vous testez.

var sum = 0
(0 to 1000).iterator.takeWhile(_ => sum < 1000).foreach(i => sum+=i)

(attention-cela dépend des détails de la façon dont le test takeWhile et le foreach sont entrelacés pendant l'évaluation, et probablement ne devrait pas être utilisé dans la pratique!).

(1b) utilisez la récursivité de la queue au lieu d'une boucle for, en profitant de la facilité d'écriture d'une nouvelle méthode dans Scala:

var sum = 0
def addTo(i: Int, max: Int) {
  sum += i; if (sum < max) addTo(i+1,max)
}
addTo(0,1000)

(1c) revenir à l'utilisation d'une boucle while

var sum = 0
var i = 0
while (i <= 1000 && sum <= 1000) { sum += 1; i += 1 }

(2) Lancer une exception.

object AllDone extends Exception { }
var sum = 0
try {
  for (i <- 0 to 1000) { sum += i; if (sum>=1000) throw AllDone }
} catch {
  case AllDone =>
}

(2a) dans Scala 2.8 + ceci est déjà pré-emballé dans scala.util.control.Breaks en utilisant une syntaxe qui ressemble beaucoup à votre ancienne pause familière de C / Java:

import scala.util.control.Breaks._
var sum = 0
breakable { for (i <- 0 to 1000) {
  sum += i
  if (sum >= 1000) break
} }

(3) Mettez le code dans une méthode et utilisez retourner.

var sum = 0
def findSum { for (i <- 0 to 1000) { sum += i; if (sum>=1000) return } }
findSum

Ceci est intentionnellement Rendu pas trop facile pour au moins trois raisons auxquelles je peux penser. Tout d'abord, dans les grands blocs de code, il est facile d'ignorer les instructions "continue" et "break", ou de penser que vous sortez de plus ou moins que vous ne l'êtes vraiment, ou d'avoir besoin de casser deux boucles que vous ne pouvez pas faire facilement de toute façon-donc l'utilisation standard, bien que pratique, a ses problèmes, et Deuxièmement, Scala a toutes sortes de niches que vous probablement ne remarquez même pas, donc si vous pouviez sortir des choses, vous seriez probablement surpris par l'endroit où le flux de code a fini (en particulier avec les fermetures). Troisièmement, la plupart des "boucles" de Scala ne sont pas réellement des boucles normales-ce sont des appels de méthode qui ont leur propre boucle, ou ils sont une récursivité qui peut ou non être une boucle-et bien qu'ils agissent looplike, il est difficile de trouver un moyen cohérent de savoir ce que "break" et autres devraient faire. Donc, pour être cohérent, la chose la plus sage de faire est de ne pas avoir une "pause" du tout.

Note: Il existe des équivalents fonctionnels de tous ceux-ci où vous renvoyez la valeur de sum plutôt que de la muter en place. Ce sont des Scala plus idiomatiques. Cependant, la logique reste la même. (return devient return x, etc.).

332
répondu Rex Kerr 2016-11-10 20:03:56

Cela a changé dans Scala 2.8 qui a un mécanisme pour utiliser les pauses. Vous pouvez maintenant faire ce qui suit:

import scala.util.control.Breaks._
var largest = 0
// pass a function to the breakable method
breakable { 
    for (i<-999 to 1  by -1; j <- i to 1 by -1) {
        val product = i * j
        if (largest > product) {
            break  // BREAK!!
        }
        else if (product.toString.equals(product.toString.reverse)) {
            largest = largest max product
        }
    }
}
57
répondu hohonuuli 2016-06-15 20:23:59

, Il n'est jamais une bonne idée de sortir d'une boucle for. Si vous utilisez une boucle for, cela signifie que vous savez combien de fois vous voulez itérer. Utilisez une boucle while avec 2 conditions.

Par exemple

var done = false
while (i <= length && !done) {
  if (sum > 1000) {
     done = true
  }
}
23
répondu Keith Blanchard 2018-06-01 16:47:09

Pour ajouter Rex Kerr, répondez d'une autre manière:

  • (1c) Vous pouvez également utiliser un garde dans votre boucle:

     var sum = 0
     for (i <- 0 to 1000 ; if sum<1000) sum += i
    
12
répondu Patrick 2010-04-30 07:56:39

Comme il n'y a pas encore de break dans Scala, vous pouvez essayer de résoudre ce problème en utilisant une return-statement. Par conséquent, vous devez mettre votre boucle interne dans une fonction, sinon le retour sauterait toute la boucle.

Scala 2.8 inclut cependant un moyen de casser

Http://www.scala-lang.org/api/rc/scala/util/control/Breaks.html

6
répondu Ham 2011-02-10 01:38:24

Il suffit d'utiliser une boucle while:

var (i, sum) = (0, 0)
while (sum < 1000) {
  sum += i
  i += 1
}
5
répondu pathikrit 2014-04-21 22:38:12
// import following package
import scala.util.control._

// create a Breaks object as follows
val loop = new Breaks;

// Keep the loop inside breakable as follows
loop.breakable{
// Loop will go here
for(...){
   ....
   // Break will go here
   loop.break;
   }
}

Utiliser le module pause http://www.tutorialspoint.com/scala/scala_break_statement.htm

4
répondu user1836270 2014-03-10 04:37:17

Une approche qui génère les valeurs sur une plage au fur et à mesure que nous itérons, jusqu'à une condition de rupture, au lieu de générer d'abord une plage entière et ensuite itérer dessus, en utilisant Iterator, (inspiré par @RexKerr utilisation de Stream)

var sum = 0
for ( i <- Iterator.from(1).takeWhile( _ => sum < 1000) ) sum += i
4
répondu elm 2015-02-12 10:17:30

Voici une version récursive de queue. Comparé aux For-compréhensions, c'est un peu cryptique, certes, mais je dirais que c'est fonctionnel:)

def run(start:Int) = {
  @tailrec
  def tr(i:Int, largest:Int):Int = tr1(i, i, largest) match {
    case x if i > 1 => tr(i-1, x)
    case _ => largest
  }

  @tailrec
  def tr1(i:Int,j:Int, largest:Int):Int = i*j match {
    case x if x < largest || j < 2 => largest
    case x if x.toString.equals(x.toString.reverse) => tr1(i, j-1, x)
    case _ => tr1(i, j-1, largest)
  }

  tr(start, 0)
}

Comme vous pouvez le voir, la fonction tr est la contrepartie des For-compréhensions externes, et tr1 de la fonction interne. Vous êtes les bienvenus si vous connaissez un moyen d'optimiser ma version.

3
répondu fresskoma 2011-06-06 00:20:40

Près de votre solution serait ceci:

var largest = 0
for (i <- 999 to 1 by -1;
  j <- i to 1 by -1;
  product = i * j;
  if (largest <= product && product.toString.reverse.equals (product.toString.reverse.reverse)))
    largest = product

println (largest)

L'itération j est faite sans nouvelle portée, et la génération de produit ainsi que la condition sont faites dans l'instruction for (pas une bonne expression-je n'en trouve pas une meilleure). La condition est inversée, ce qui est assez rapide pour cette taille de problème-peut-être que vous gagnez quelque chose avec une pause pour les boucles plus grandes.

Chaîne.reverse se convertit implicitement en RichString, c'est pourquoi je fais 2 inversions supplémentaires. :) Un plus mathématique approche pourrait être plus élégant.

2
répondu user unknown 2010-05-22 14:45:27

Le paquet tiers breakable est une alternative possible

Https://github.com/erikerlandson/breakable

Exemple de 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))
2
répondu eje 2017-03-05 19:58:44

Ironiquement, la rupture Scala dans scala.util.control.Breaks est une exception:

def break(): Nothing = { throw breakException }

Le meilleur conseil est: NE PAS utiliser Pause, Continuer et goto! IMO ils sont les mêmes, mauvaise pratique et une source maléfique de toutes sortes de problèmes (et des discussions chaudes) et enfin "considéré comme nuisible". Bloc de Code structuré, également dans cet exemple, les pauses sont superflues. Notre Edsger W. Dijkstra† a écrit:

La qualité des programmeurs est une fonction décroissante de la densité des instructions go to dans les programmes qu'ils produire.

1
répondu Epicurist 2014-06-23 08:19:06

Astucieux utiliser find méthode pour la collecte va faire l'affaire pour vous.

var largest = 0
lazy val ij =
  for (i <- 999 to 1 by -1; j <- i to 1 by -1) yield (i, j)

val largest_ij = ij.find { case(i,j) =>
  val product = i * j
  if (product.toString == product.toString.reverse)
    largest = largest max product
  largest > product
}

println(largest_ij.get)
println(largest)
1
répondu AlvaPan 2016-02-26 19:27:11

J'ai une situation comme le code ci-dessous

 for(id<-0 to 99) {
    try {
      var symbol = ctx.read("$.stocks[" + id + "].symbol").toString
      var name = ctx.read("$.stocks[" + id + "].name").toString
      stocklist(symbol) = name
    }catch {
      case ex: com.jayway.jsonpath.PathNotFoundException=>{break}
    }
  }

J'utilise une lib java et le mécanisme est que ctx.lire jeter une Exception quand il ne peut rien trouver. J'ai été pris au piège dans la situation que: je dois briser la boucle quand une Exception a été lancée, mais scala.util.contrôle.Briser.pause en utilisant Exception pour briser la boucle, et il était dans le bloc catch ainsi il a été attrapé.

J'ai eu un moyen moche de résoudre ceci: faites la boucle pour la première fois et obtenez le compte de la longueur réelle. et de l'utiliser pour la deuxième boucle.

Prendre une pause de Scala n'est pas si bon, lorsque vous utilisez des libs java.

0
répondu Lucas Liu 2015-10-08 10:23:18

Je suis nouveau à Scala, mais que diriez-vous de cela pour éviter de lancer des exceptions et de répéter des méthodes:

object awhile {
def apply(condition: () => Boolean, action: () => breakwhen): Unit = {
    while (condition()) {
        action() match {
            case breakwhen(true)    => return ;
            case _                  => { };
        }
    }
}
case class breakwhen(break:Boolean);

Utilisez-le comme ceci:

var i = 0
awhile(() => i < 20, () => {
    i = i + 1
    breakwhen(i == 5)
});
println(i)

Si vous ne voulez pas casser:

awhile(() => i < 20, () => {
    i = i + 1
    breakwhen(false)
});
0
répondu Mohamad Alallan 2016-01-21 09:19:37
import scala.util.control._

object demo_brk_963 
{
   def main(args: Array[String]) 
   {
      var a = 0;
      var b = 0;
      val numList1 = List(1,2,3,4,5,6,7,8,9,10);
      val numList2 = List(11,12,13);

      val outer = new Breaks; //object for break
      val inner = new Breaks; //object for break

      outer.breakable // Outer Block
      {
         for( a <- numList1)
         {
            println( "Value of a: " + a);

            inner.breakable // Inner Block
            {
               for( b <- numList2)
               {
                  println( "Value of b: " + b);

                  if( b == 12 )
                  {
                      println( "break-INNER;");
                       inner.break;
                  }
               }
            } // inner breakable
            if( a == 6 )
            {
                println( "break-OUTER;");
                outer.break;
            }
         }
      } // outer breakable.
   }
}

Méthode de base pour briser la boucle, en utilisant la classe Breaks. En déclarant la boucle comme cassable.

0
répondu Parvathi Rajan 2018-05-10 10:22:22

Simplement, nous pouvons faire dans scala est

scala> import util.control.Breaks._

scala> object TestBreak{
       def main(args : Array[String]){
       breakable {
       for (i <- 1 to 10){
       println(i)
       if (i == 5){
       break;
       } } } } }

Sortie :

scala> TestBreak.main(Array())
1
2
3
4
5
0
répondu Viraj.Hadoop 2018-07-20 11:56:04