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?
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.).
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
}
}
}
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
}
}
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
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
Verwenden Sie einfach eine while-Schleife:
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;
}
}
verwenden Sie Break module http://www.tutorialspoint.com/scala/scala_break_statement.htm
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
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.
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))
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
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.
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)
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"
}
}
}
}
}
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.
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.
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.
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)
});
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.