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?
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.).
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
}
}
}
, 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
}
}
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
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
Il suffit d'utiliser une boucle while:
var (i, sum) = (0, 0)
while (sum < 1000) {
sum += i
i += 1
}
// 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
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
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.
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.
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))
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.
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)
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.
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)
});
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.
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