Как мне вырваться из петли в Scala?
Как мне разорвать цикл?
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
}
}
Как превратить вложенные циклы for в хвостовую рекурсию?
из Scala Talk на FOSDEM 2009 http://www.slideshare.net/Odersky/fosdem-2009-1013261 на 22-й странице:
Break и continue У скалы их нет. Зачем? Они немного императивны; лучше использовать много меньших функций Вопрос, как взаимодействовать с закрытиями. Они не нужны!
что такое объяснение?
17 ответов:
у вас есть три (или около того) вариантов, чтобы вырваться из петли.
Предположим, вы хотите суммировать числа до тех пор, пока общая сумма не превысит 1000. Ты попробуй
var sum = 0 for (i <- 0 to 1000) sum += i
за исключением того, что вы хотите остановиться, когда (сумма > 1000).
что делать? Есть несколько вариантов.
(1a) используйте некоторую конструкцию, которая включает в себя условие, которое вы тестируете.
var sum = 0 (0 to 1000).iterator.takeWhile(_ => sum < 1000).foreach(i => sum+=i)
(предупреждение--это зависит от деталей того, как тест takeWhile и foreach перемежаются во время оценки, и, вероятно, не должны использоваться на практике!).
(1b) используйте хвостовую рекурсию вместо цикла for, пользуясь тем, как легко написать новый метод в Scala:
var sum = 0 def addTo(i: Int, max: Int) { sum += i; if (sum < max) addTo(i+1,max) } addTo(0,1000)
(1c) вернуться к использованию цикла while
var sum = 0 var i = 0 while (i <= 1000 && sum <= 1000) { sum += 1; i += 1 }
(2) исключение.
object AllDone extends Exception { } var sum = 0 try { for (i <- 0 to 1000) { sum += i; if (sum>=1000) throw AllDone } } catch { case AllDone => }
(2a) в Scala 2.8+ это уже предварительно упаковано в
scala.util.control.Breaks
используя синтаксис, который очень похож на ваш знакомый старый перерыв C / Java:import scala.util.control.Breaks._ var sum = 0 breakable { for (i <- 0 to 1000) { sum += i if (sum >= 1000) break } }
(3) Поместите код в метод и используйте return.
var sum = 0 def findSum { for (i <- 0 to 1000) { sum += i; if (sum>=1000) return } } findSum
это намеренно не слишком легко, по крайней мере, по трем причинам, я думаю. Во-первых, в больших блоках кода легко пропустить операторы "продолжить" и "разбить", или думать, что вы выходите из более или менее, чем вы есть на самом деле, или вам нужно разбить два цикла, которые вы не можете сделать легко в любом случае-так что стандартное использование, хотя и удобно, имеет свои проблемы, и поэтому вы должны попытаться структурируйте свой код по-другому. Во-вторых, Scala имеет всевозможные вложения, которые вы, вероятно, даже не замечаете, поэтому, если бы вы могли вырваться из вещей, вы, вероятно, были бы удивлены тем, где поток кода оказался (особенно с закрытиями). В-третьих, большинство "циклов" Scala на самом деле не являются нормальными циклами-это вызовы методов, которые имеют свой собственный цикл, или они являются рекурсией, которая может быть или не быть циклом-и хотя они акт looplike, трудно придумать последовательный способ узнать, что такое" перерыв " и тому подобное. Поэтому, чтобы быть последовательным, разумнее всего вообще не иметь "перерыва".
Примечание: есть функциональные эквиваленты всех из них, где вы возвращаете значение
sum
вместо того, чтобы мутировать его на месте. Это более идиоматические Scala. Однако логика остается прежней. (return
становитсяreturn x
и т. д.).
это изменилось в Scala 2.8, который имеет механизм для использования перерывов. Теперь вы можете сделать следующее:
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 } } }
Это никогда не хорошая идея, чтобы вырваться из цикла. Если вы используете цикл for, это означает, что вы знаете, сколько раз вы хотите повторить. Использовать цикл while с 2 условиями.
var done = false while (i <= length && !done) { if (sum > 1000) { done = true } }
чтобы добавить ответ Рекса Керра другим способом:
(1c) вы также можете использовать защиту в вашем цикле:
var sum = 0 for (i <- 0 to 1000 ; if sum<1000) sum += i
Так как нет
break
в Scala еще можно попробовать решить эту проблему с помощьюreturn
-заявление. Поэтому вам нужно поместить свой внутренний цикл в функцию, иначе возврат пропустит весь цикл.Scala 2.8 однако включает в себя способ сломать
http://www.scala-lang.org/api/rc/scala/util/control/Breaks.html
// 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; } }
использовать пауза модуль http://www.tutorialspoint.com/scala/scala_break_statement.htm
подход, который генерирует значения в диапазоне по мере итерации, вплоть до критического состояния, вместо того, чтобы сначала генерировать весь диапазон, а затем повторять его, используя
Iterator
, (вдохновленный в @RexKerr использованиеStream
)var sum = 0 for ( i <- Iterator.from(1).takeWhile( _ => sum < 1000) ) sum += i
вот хвост рекурсивной версии. По сравнению с for-comprehensions это немного загадочно, по общему признанию, но я бы сказал, что его функционал :)
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) }
Как вы можете видеть, функция tr является аналогом внешнего понимания, а tr1-внутреннего. Добро пожаловать, если вы знаете способ оптимизировать мой вариант.
близко к вашему решению будет это:
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)
J-итерация выполняется без новой области, а генерация продукта, а также условие выполняются в операторе for (не очень хорошее выражение-я не нахожу лучшего). Условие отменяется, что довольно быстро для этого размера проблемы-возможно, вы получаете что-то с перерывом на большие петли.
строку.обратный неявно преобразуется в RichString, поэтому я делаю 2 дополнительных разворота. :) Еще математический подход может быть более элегантным.
третьих лиц
breakable
пакета является одной из возможных альтернативhttps://github.com/erikerlandson/breakable
пример кода:
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))
по иронии судьбы Scala перерыв в
scala.util.control.Breaks
исключение:def break(): Nothing = { throw breakException }
лучший совет: не используйте перерыв, продолжить и Гото! ИМО они одинаковы, плохая практика и злой источник всех видов проблем (и горячих дискуссий) и, наконец, "считаются вредными". Блок кода структурирован, также в этом примере разрывы излишни. Наш Edsger W. Dijkstra† писал:
качество программистов-это убывающая функция плотности перехода к отчетность в программах, которые они производят.
умный использование
find
метод для сбора сделает трюк для вас.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)
Я получил ситуацию, как код ниже
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} } }
Я использую Java lib и механизм заключается в том, что ctx.читайте бросить исключение, когда он не может найти ничего. Я оказался в ловушке ситуации, что: я должен разорвать цикл, когда исключение было брошено, но scala.утиль.управление.Разрыв.перерыв с помощью исключения, чтобы разорвать цикл, и он был в блоке catch, таким образом, он был пойман.
Я получил уродливый способ решить эту проблему: сделать цикл в первый раз и получить количество реальных длина. и используйте его для второго цикла.
вынуть перерыв из Scala не так хорошо, когда вы используете некоторые Java-библиотеки.
Я новичок в Scala, но как насчет этого, чтобы не бросать исключения и повторять методы:
object awhile { def apply(condition: () => Boolean, action: () => breakwhen): Unit = { while (condition()) { action() match { case breakwhen(true) => return ; case _ => { }; } } } case class breakwhen(break:Boolean);
используйте его так:
var i = 0 awhile(() => i < 20, () => { i = i + 1 breakwhen(i == 5) }); println(i)
Если вы не хотите сломать:
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. } }
основной метод для разрыва цикла, используя класс Breaks. Объявив цикл разрываемым.