Как вырвать цикл?
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
}
}
Как включить вложенные для циклов в хвостовую рекурсию?
Из Scala Обсуждение в FOSDEM 2009 http://www.slideshare.net/Odersky/fosdem-2009-1013261 на 22-й странице:
Перерыв и продолжение Scala не имеет их. Зачем? Они немного необходимы; лучше использовать многие более мелкие функции Выясните, как взаимодействовать с закрытием. Они не нужны!
Какое объяснение?
У вас есть три (или около того) варианта, чтобы вырваться из циклов.
Предположим, вы хотите суммировать числа, пока общее количество больше 1000. Вы пытаетесь
var sum = 0
for (i <- 0 to 1000) sum += i
кроме того, что вы хотите остановить, когда (суммa > 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 "на самом деле не являются нормальными циклами - это вызовы методов, которые имеют свой собственный цикл, или они являются рекурсией, которая может быть или не быть фактически петлей, - и хотя они действуют петлеобразно, трудно найти последовательный способ узнать, что делать" перерыв "и тому подобное. Итак, чтобы быть последовательным, разумнее всего не делать" перерыва" вообще.
Примечание. Существуют функциональные эквиваленты всех этих элементов, в которых вы возвращаете значение sum
вместо того, чтобы мутировать его на месте. Они более идиоматичны Scala. Однако логика остается прежней. (return
становится return x
и т.д.).
return
явно.
Это изменилось в 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. Если вы используете цикл for, это означает, что вы знаете, сколько раз вы хотите итерации. Используйте цикл while с двумя условиями.
например
var done = false
while (i <= length && !done) {
if (i > 1000) {
done = true;
}
Чтобы добавить Рекса Керра другим способом:
(1c) Вы также можете использовать охрану в своем цикле:
var sum = 0
for (i <- 0 to 1000 ; if sum<1000) sum += i
Так как в Scala нет break
, вы можете попытаться решить эту проблему с помощью return
-statement. Поэтому вам нужно поместить ваш внутренний цикл в функцию, иначе возврат будет пропускать весь цикл.
Scala 2.8 однако включает способ разрыва
http://www.scala-lang.org/api/rc/scala/util/control/Breaks.html
Просто используйте цикл 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;
}
}
использовать модуль 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
Вот хвостовая рекурсивная версия. По общему мнению, это немного загадочно, но я бы сказал, его функциональность:)
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 внутреннего. Добро пожаловать, если вы знаете способ оптимизации моей версии.
Сторонний пакет 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))
Близко к вашему решению будет следующее:
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-statement (не хорошее выражение - я не нахожу лучшего). Условие отменено, что довольно быстро для этого размера проблемы - возможно, вы получаете что-то с перерывом для больших циклов.
String.reverse неявно преобразуется в RichString, поэтому я делаю 2 дополнительных обратных.:) Более математический подход может быть более изящным.
Как ни странно, Scala break in scala.util.control.Breaks
является исключением:
def break(): Nothing = { throw breakException }
Лучший совет: НЕ используйте break, continue и goto! ИМО - это то же самое, плохая практика и злой источник всех проблем (и горячие дискуссии) и, наконец, "считаются вредными". Структурированный блок кода, также в этом примере перерывы являются излишними. Наш 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)
Я новичок в 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)
});
Появилась ситуация, подобная приведенному ниже коду
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.read выдает исключение, когда он ничего не может найти. Я оказался в ловушке ситуации: я должен разбить цикл, когда был выброшен Exception, но scala.util.control.Breaks.break, используя Exception, чтобы разбить цикл, и он был в блоке catch, таким образом, он был пойман.
Я получил уродливый способ решить это: сделать цикл в первый раз и получить счетчик реальной длины. и использовать его для второго цикла.
вывести разрыв из Scala не так уж и хорошо, когда вы используете некоторые java-библиотеки.