Как выйти из цикла в 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
    }
}

Как включить вложенные для циклов в хвостовую рекурсию?

Из Scala Обсуждение в FOSDEM 2009 http://www.slideshare.net/Odersky/fosdem-2009-1013261 на 22-й странице:

Перерыв и продолжение Scala не имеет их. Зачем? Они немного необходимы; лучше использовать многие более мелкие функции Выясните, как взаимодействовать с закрытием. Они не нужны!

Какое объяснение?

Ответ 1

У вас есть три (или около того) варианта, чтобы вырваться из циклов.

Предположим, вы хотите суммировать числа, пока общее количество больше 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 и т.д.).

Ответ 2

Это изменилось в 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
        }
    }
}

Ответ 3

Это никогда не хорошая идея, чтобы вырваться из цикла. Если вы используете цикл for, это означает, что вы знаете, сколько раз вы хотите выполнить итерацию. Используйте цикл while с 2 условиями.

например

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

Ответ 4

Чтобы добавить Рекса Керра другим способом:

  • (1c) Вы также можете использовать охрану в своем цикле:

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

Ответ 5

Так как в Scala нет break, вы можете попытаться решить эту проблему с помощью return -statement. Поэтому вам нужно поместить ваш внутренний цикл в функцию, иначе возврат будет пропускать весь цикл.

Scala 2.8 однако включает способ разрыва

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

Ответ 6

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

Ответ 7

Просто используйте цикл while:

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

Ответ 8

Подход, который генерирует значения над диапазоном по мере того, как мы итерации, вплоть до условия разбиения, вместо генерации сначала целого диапазона, а затем итерации по нему, используя Iterator (вдохновленный использованием @RexKerr Stream)

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

Ответ 9

Вот хвостовая рекурсивная версия. По общему мнению, это немного загадочно, но я бы сказал, его функциональность:)

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 внутреннего. Добро пожаловать, если вы знаете способ оптимизации моей версии.

Ответ 10

Близко к вашему решению будет следующее:

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 дополнительных обратных.:) Более математический подход может быть более изящным.

Ответ 11

Сторонний пакет 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)] = [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))

Ответ 12

Просто мы можем сделать это в

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

вывод:

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

Ответ 13

Как ни странно, Scala break in scala.util.control.Breaks является исключением:

def break(): Nothing = { throw breakException }

Лучший совет: НЕ используйте break, continue и goto! ИМО - это то же самое, плохая практика и злой источник всех проблем (и горячие дискуссии) и, наконец, "считаются вредными". Структурированный блок кода, также в этом примере перерывы являются излишними. Наш Edsger W. Dijkstra † написал:

Качество программистов - это уменьшающаяся функция плотности перехода к операторам в программах, которые они создают.

Ответ 14

Умное использование метода 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)

Ответ 15

  Ниже приведен код для простого разрыва цикла

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"

        }
      }
    }
  }
}

Ответ 16

Я не знаю, насколько сильно изменился стиль Scala за последние 9 лет, но мне показалось интересным, что большинство существующих ответов используют vars или трудно читаемую рекурсию. Ключом к раннему выходу является использование ленивого набора для генерации возможных кандидатов, а затем проверка состояния отдельно. Для создания продуктов:

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

Затем, чтобы найти первый палиндром из этого представления, не генерируя каждую комбинацию:

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

Чтобы найти самый большой палиндром (хотя лень не стоит вам много, потому что вы все равно должны проверить весь список):

palindromes.max

Ваш исходный код фактически проверяет первый палиндром, который больше, чем последующий продукт, что аналогично проверке первого палиндрома, за исключением странного граничного условия, которое, как я думаю, вы не предполагали. Продукты не являются строго монотонно убывающими. Например, 998*998 больше, чем 999*997, но появляется намного позже в циклах.

В любом случае, преимущество раздельной проверки ленивости и условий заключается в том, что вы пишете ее почти так же, как и весь список, но она генерирует столько, сколько вам нужно. Вы как бы получаете лучшее из обоих миров.

Ответ 17

Появилась ситуация, подобная приведенному ниже коду

 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-библиотеки.

Ответ 18

Я новичок в 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)
});

Ответ 19

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. Объявляя цикл как разрушаемый.