web-dev-qa-db-fra.com

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 des boucles imbriquées en récursion de queue?

Extrait de Scala Talk à FOSDEM 2009 http://www.slideshare.net/Odersky/fosdem-2009-1013261 À la 22ème page:

Pause et continue Scala ne les a pas. Pourquoi? Ils sont un peu impératifs; mieux utiliser de nombreuses fonctions plus petites Indiquez comment interagir avec les fermetures ..__ Ils ne sont pas nécessaires!

Quelle est l'explication?

251
TiansHUo

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

Supposons que vous vouliez additionner les 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 une condition 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 manière dont le test takeWhile et le foreach sont entrelacés lors de l'évaluation et ne devraient probablement pas être utilisés dans la pratique!).

(1b) Utilisez une récursion de queue au lieu d'une boucle for, en tirant parti de la facilité avec laquelle il est facile d'écrire 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) Utiliser 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+, cela est déjà pré-emballé dans scala.util.control.Breaks en utilisant une syntaxe qui ressemble beaucoup à votre ancienne rupture familière avec 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 return.

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

Cela est intentionnellement rendu pas trop facile pour au moins trois raisons auxquelles je peux penser. Premièrement, dans les grands blocs de code, il est facile d'oublier les déclarations "continue" et "cassée", ou de penser que vous vous échappez de plus ou de moins que ce que vous êtes réellement, ou de devoir rompre deux boucles que vous ne pouvez pas faire. facilement quand même - ainsi l’utilisation standard, bien que pratique, a ses problèmes, et vous devriez donc essayer de structurer votre code d’une manière différente. Deuxièmement, Scala a toutes sortes de imbrications que vous ne remarquerez probablement même pas. Si vous pouviez vous en sortir, vous seriez probablement surpris de savoir où le flux de code a abouti (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 une récursion qui peut être ou non une boucle - et bien qu'ils act Comme c'est le cas, il est difficile de trouver un moyen cohérent de savoir ce que «casser», etc., devrait faire. Donc, pour être cohérent, la meilleure chose à faire est de ne pas faire de "pause" du tout.

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

348
Rex Kerr

Cela a changé dans Scala 2.8, qui dispose d’un mécanisme d’utilisation des 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
        }
    }
}
60
hohonuuli

Ce n'est jamais une bonne idée de sortir d'une boucle perdue. Si vous utilisez une boucle for, cela signifie que vous savez combien de fois vous souhaitez effectuer une itération. Utilisez une boucle while avec 2 conditions. 

par exemple

var done = false
while (i <= length && !done) {
  if (sum > 1000) {
     done = true
  }
}
27
Keith Blanchard

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

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

     var sum = 0
     for (i <- 0 to 1000 ; if sum<1000) sum += i
    
12
Patrick

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

Scala 2.8 comprend toutefois un moyen de rompre

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

6
Ham

Il suffit d'utiliser une boucle while:

var (i, sum) = (0, 0)
while (sum < 1000) {
  sum += i
  i += 1
}
5
pathikrit
// 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;
   }
}

utilisez le module Break http://www.tutorialspoint.com/scala/scala_break_statement.htm

5
user1836270

Une approche qui génère les valeurs sur une plage lorsque nous itérons, jusqu'à une condition de rupture, au lieu de générer d'abord une plage entière puis de la parcourir, en utilisant Iterator, (inspiré de l'utilisation de @RexKerr par Stream)

var sum = 0
for ( i <- Iterator.from(1).takeWhile( _ => sum < 1000) ) sum += i
4
elm

Voici une version récursive de la queue. Comparé aux for-compréhensions, il 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 constater, la fonction tr est la contrepartie de la compréhension externe et la valeur tr1 de la compréhension interne. Vous êtes le bienvenu si vous connaissez un moyen d'optimiser ma version.

3
fresskoma

Le package breakable tiers 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
eje

Nous pouvons simplement faire en 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
2
Viraj.Hadoop

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)

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

String.reverse est converti implicitement en RichString, raison pour laquelle j'ai effectué 2 inversions supplémentaires. :) Une approche plus mathématique pourrait être plus élégante. 

2
user unknown

Clever utilisation de la méthode find pour la collecte fera 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
AlvaPan

Ci-dessous le code pour casser une boucle de manière simple

import scala.util.control.Breaks.break

object RecurringCharacter {
  def main(args: Array[String]) {
    val str = "nileshshinde";

    for (i <- 0 to str.length() - 1) {
      for (j <- i + 1 to str.length() - 1) {

        if (str(i) == str(j)) {
          println("First Repeted Character " + str(i))
          break()     //break method will exit the loop with an Exception "Exception in thread "main" scala.util.control.BreakControl"

        }
      }
    }
  }
}
1
Nilesh Shinde

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

def break(): Nothing = { throw breakException }

Le meilleur conseil est: NE PAS utiliser break, continue et goto! OMI, ce sont les mêmes, une mauvaise pratique et une source perverse de toutes sortes de problèmes (et de discussions passionnées) et enfin "considérés comme nuisibles". Le bloc de code structuré, également dans cet exemple, les ruptures sont superflues . Notre Edsger W. Dijkstra † a écrit:

La qualité des programmeurs est une fonction décroissante de la densité des instructions dans les programmes qu’ils produisent.

1
Epicurist

J'ai eu 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 librairie Java et le mécanisme consiste en ce que ctx.read lève une exception lorsqu'il ne trouve rien .. .. J'ai été pris au piège dans la situation suivante: je dois rompre la boucle lorsqu'une exception est lancée, mais scala.util .control.Breaks.break utilisant Exception pour casser la boucle, et comme il était dans le bloc catch, il a été attrapé.

J'ai eu la mauvaise façon de résoudre ceci: faire la boucle pour la première fois et obtenir le décompte de la longueur réelle. et l’utiliser pour la deuxième boucle.

sortir de Scala n'est pas très bon quand on utilise des librairies Java.

0
Lucas Liu

Je ne sais pas à quel point le style de Scala a changé au cours des 9 dernières années, mais j'ai trouvé intéressant que la plupart des réponses existantes utilisent vars, ou une récursion difficile à lire. La clé de la sortie anticipée consiste à utiliser une collection paresseuse pour générer vos candidats potentiels, puis à vérifier la condition séparément. Pour générer les produits:

val products = for {
  i <- (999 to 1 by -1).view
  j <- (i to 1 by -1).view
} yield (i*j)

Ensuite, pour trouver le premier palindrome à partir de cette vue sans générer toutes les combinaisons:

val palindromes = products filter {p => p.toString == p.toString.reverse}
palindromes.head

Pour trouver le plus grand palindrome (bien que la paresse ne vous achète pas beaucoup car vous devez quand même vérifier toute la liste):

palindromes.max

Votre code d'origine recherche en fait le premier palindrome plus grand qu'un produit ultérieur, ce qui revient à rechercher le premier palindrome, sauf dans une condition limite étrange que je ne pensais pas être votre intention. Les produits ne sont pas strictement décroissants. Par exemple, 998*998 est supérieur à 999*997, mais apparaît beaucoup plus tard dans les boucles.

En tout état de cause, l’avantage de la génération différée séparée et de la vérification de condition est que vous l’écrivez à peu près comme si vous utilisiez la liste complète, mais elle ne génère que ce dont vous avez besoin. Vous obtenez en quelque sorte le meilleur des deux mondes.

0
Karl Bielefeldt

Je suis nouveau sur Scala, mais que diriez-vous de cela pour éviter de jeter 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 faire une pause:

awhile(() => i < 20, () => {
    i = i + 1
    breakwhen(false)
});
0
Mohamad Alallan
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 rompre la boucle, en utilisant la classe Breaks ..__, en déclarant la boucle cassable.