it-swarm.com.de

Wie kann ich in Scala aus einer Schleife ausbrechen?

Wie kann ich eine Schleife ausbrechen?

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
    }
}

Wie kann ich verschachtelte Schleifen in eine Rekursion des Endes umwandeln?

Aus Scala Talk auf der FOSDEM 2009 http://www.slideshare.net/Odersky/fosdem-2009-1013261 Auf der 22. Seite:

Pause und weiter Scala hat sie nicht. Warum? Sie sind ein bisschen zwingend; Verwenden Sie besser viele kleinere Funktionen Frage, wie man mit Verschlüssen interagieren soll . Sie werden nicht benötigt!

Was ist die Erklärung?

251
TiansHUo

Sie haben drei (oder so) Optionen, um Loops zu durchbrechen.

Angenommen, Sie möchten Zahlen summieren, bis die Gesamtsumme größer als 1000 ist. Sie versuchen es

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

außer Sie möchten aufhören, wenn (Summe> 1000).

Was ist zu tun? Es gibt mehrere Möglichkeiten.

(1a) Verwenden Sie ein Konstrukt, das eine Bedingung enthält, die Sie testen.

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

(Warnung - dies hängt davon ab, wie der takeWhile-Test und der foreach während der Bewertung verschachtelt werden und sollten in der Praxis wahrscheinlich nicht verwendet werden!).

(1b) Verwenden Sie die Tail-Rekursion anstelle einer for-Schleife, um die einfache Methode zu nutzen, eine neue Methode in Scala zu schreiben:

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

(1c) Verwenden Sie eine while-Schleife

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

(2) Eine Ausnahme auslösen.

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

(2a) In Scala 2.8 und höher ist dies bereits in scala.util.control.Breaks mit einer Syntax gepackt, die Ihrem alten Break von C/Java sehr ähnlich sieht:

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

(3) Fügen Sie den Code in eine Methode ein und verwenden Sie return.

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

Dies ist aus mindestens drei Gründen, die ich mir vorstellen kann, absichtlich nicht allzu leicht gemacht. Erstens: In großen Codeblöcken können Sie leicht "Continue" - und "Break" -Anweisungen übersehen oder denken, dass Sie mehr oder weniger als Sie wirklich brechen, oder zwei Schleifen brechen müssen, was Sie nicht tun können leicht ohnehin leicht - so hat die Standardanwendung, obwohl sie praktisch ist, ihre Probleme, und daher sollten Sie versuchen, Ihren Code anders zu strukturieren. Zweitens, Scala hat alle möglichen Arten von Schachteln, die Sie wahrscheinlich nicht einmal bemerken. Wenn Sie also ausbrechen könnten, würden Sie wahrscheinlich überrascht sein, wo der Code-Fluss endete (besonders bei Schließungen). Drittens sind die meisten "Schleifen" von Scala keine normalen Schleifen - sie sind Methodenaufrufe, die ihre eigene Schleife haben, oder Rekursion, die tatsächlich eine Schleife sein kann oder auch nicht - und obwohl sie act sind. Es ist schwer, eine konsistente Methode zu finden, um zu wissen, was "break" und dergleichen tun sollen. Um konsequent zu sein, ist es weiser, überhaupt keine "Pause" zu machen.

Hinweis : Von allen gibt es funktionale Äquivalente, bei denen Sie den Wert von sum zurückgeben, anstatt ihn an Ort und Stelle zu mutieren. Das sind mehr idiomatische Scala. Die Logik bleibt jedoch gleich. (return wird zu return x usw.).

348
Rex Kerr

Dies hat sich in Scala 2.8 geändert, das über einen Mechanismus zur Verwendung von Pausen verfügt. Sie können jetzt Folgendes tun:

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

Es ist nie eine gute Idee, aus einer for-Schleife auszubrechen. Wenn Sie eine for-Schleife verwenden, wissen Sie, wie oft Sie iterieren möchten. Verwenden Sie eine while-Schleife mit 2 Bedingungen. 

zum Beispiel

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

Um Rex Kerr Antwort auf andere Weise hinzuzufügen:

  • (1c) Sie können auch eine Wache in Ihrer Schleife verwenden:

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

Da es in Scala noch keine break gibt, können Sie versuchen, dieses Problem mit einer return- Anweisung zu lösen. Daher müssen Sie Ihre innere Schleife in eine Funktion einfügen, da sonst die Rückkehr die gesamte Schleife überspringt.

Scala 2.8 beinhaltet jedoch eine Möglichkeit, zu brechen

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

6
Ham

Verwenden Sie einfach eine while-Schleife:

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;
   }
}

verwenden Sie Break module http://www.tutorialspoint.com/scala/scala_break_statement.htm

5
user1836270

Ein Ansatz, der die Werte über einen Bereich generiert, während wir iterieren, bis zu einem fehlerhaften Zustand, anstatt zuerst einen ganzen Bereich zu generieren und dann mit Iterator zu iterieren (inspiriert in @RexKerr-Verwendung von Stream).

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

Hier ist eine rekursive Version. Verglichen mit den Nachforschungen ist es zwar etwas kryptisch, aber ich würde sagen, dass es funktional ist :)

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)
}

Wie Sie sehen, ist die tr-Funktion das Gegenstück zu den äußeren Verstehen und tr1 zum inneren. Sie sind herzlich willkommen, wenn Sie wissen, wie Sie meine Version optimieren können.

3
fresskoma

Das breakable-Paket eines Drittanbieters ist eine mögliche Alternative

https://github.com/erikerlandson/breakable

Beispielcode:

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)] = [email protected]

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

Einfach können wir in Scala machen 

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;
       } } } } }

Ausgabe :

scala> TestBreak.main(Array())
1
2
3
4
5
2
Viraj.Hadoop

Nahe bei Ihrer Lösung wäre dies: 

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)

Die J-Iteration wird ohne neuen Gültigkeitsbereich ausgeführt, und die Produktgenerierung sowie die Bedingung werden in der for-Anweisung ausgeführt (kein guter Ausdruck - ich finde keinen besseren). Die Bedingung ist umgekehrt, was für diese Problemgröße ziemlich schnell ist - vielleicht gewinnen Sie etwas mit einer Pause für größere Loops. 

String.reverse konvertiert implizit in RichString, weshalb ich zwei zusätzliche Umkehrungen mache. :) Ein mathematischerer Ansatz könnte eleganter sein. 

2
user unknown

Clever Die Verwendung der find-Methode für die Sammlung erledigt den Trick für Sie. 

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

Nachfolgend finden Sie Code, um eine Schleife auf einfache Weise zu unterbrechen

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

Ironischerweise ist der Scala-Bruch in scala.util.control.Breaks eine Ausnahme:

def break(): Nothing = { throw breakException }

Der beste Rat ist: KEINE break verwenden, fortfahren und weiter gehen! IMO sind sie die gleiche, schlechte Praxis und eine böse Quelle für alle Arten von Problemen (und heißen Diskussionen) und schließlich "als schädlich betrachtet". Code-Block strukturiert, auch in diesem Beispiel sind Pausen überflüssig . Unser Edsger W. Dijkstra † schrieb:

Die Qualität der Programmierer ist eine abnehmende Funktion der Dichte der Anweisungen, die in den von ihnen erzeugten Programmen verwendet werden.

1
Epicurist

Ich habe eine Situation wie der folgende Code

 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}
    }
  }

Ich verwende eine Java-Bibliothek und der Mechanismus ist, dass ctx.read eine Exception auslöst, wenn nichts gefunden wird .. Ich war in der Situation gefangen, dass: Ich muss die Schleife unterbrechen, wenn eine Exception ausgelöst wurde, aber scala.util .control.Breaks.break verwendet Exception, um die Schleife zu unterbrechen, und es befindet sich im catch-Block, sodass er abgefangen wurde.

Ich habe einen hässlichen Weg gefunden, dies zu lösen: Machen Sie die Schleife zum ersten Mal und holen Sie sich die tatsächliche Länge. und verwende es für die zweite Schleife.

pause von Scala ist nicht so gut, wenn Sie einige Java-Bibliotheken verwenden.

0
Lucas Liu

Ich weiß nicht, wie sehr sich der Scala -Stil in den letzten 9 Jahren geändert hat, aber ich fand es interessant, dass die meisten der vorhandenen Antworten vars oder schwer lesbare Rekursionen verwenden. Der Schlüssel zum vorzeitigen Beenden besteht darin, eine verzögerte Sammlung zu verwenden, um Ihre möglichen Kandidaten zu generieren, und dann die Bedingung separat zu prüfen. So generieren Sie die Produkte:

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

Dann, um das erste Palindrom aus dieser Sicht zu finden, ohne jede Kombination zu generieren:

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

Um das größte Palindrom zu finden (obwohl die Faulheit nicht viel kostet, weil Sie sowieso die gesamte Liste überprüfen müssen):

palindromes.max

Ihr ursprünglicher Code sucht tatsächlich nach dem ersten Palindrom, das größer ist als ein nachfolgendes Produkt. Dies entspricht der Suche nach dem ersten Palindrom, mit Ausnahme einer seltsamen Randbedingung, die Sie meines Erachtens nicht beabsichtigt haben. Die Produkte sind nicht streng monoton abnehmend. Zum Beispiel ist 998*998 größer als 999*997, erscheint aber viel später in den Schleifen.

Der Vorteil der getrennten Generierungs- und Zustandsprüfung ist, dass Sie es so schreiben, als würde es die gesamte Liste verwenden, aber es wird nur so viel generiert, wie Sie benötigen. Sie bekommen sozusagen das Beste aus beiden Welten.

0
Karl Bielefeldt

Ich bin neu bei Scala, aber wie wäre es damit?

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

benutze es so:

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

wenn du nicht brechen willst:

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.
   }
}

Grundlegende Methode, um die Schleife zu unterbrechen, wobei die Breaks-Klasse ..__ verwendet wird, indem die Schleife als zerbrechlich erklärt wird.