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?
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.).
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
}
}
}
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
}
}
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
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
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;
}
}
utilisez le module Break http://www.tutorialspoint.com/scala/scala_break_statement.htm
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
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.
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))
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
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.
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)
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"
}
}
}
}
}
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.
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.
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.
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)
});
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.