Как мне вырваться из петли в 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. Объявив цикл разрываемым.