it-swarm.com.ru

Как мне выйти из цикла в 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 Talk на FOSDEM 2009 http://www.slideshare.net/Odersky/fosdem-2009-1013261 На 22-й странице:

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

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

251
TiansHUo

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

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

Это намеренно сделано не слишком легко по крайней мере по трем причинам, о которых я могу думать. Во-первых, в больших блоках кода легко пропустить операторы «continue» и «break», или думать, что вы выбиваете больше или меньше, чем вы есть на самом деле, или вам нужно разбить два цикла, которые вы не можете сделать в любом случае легко - поэтому стандартное использование, хотя и удобно, имеет свои проблемы, и поэтому вы должны попытаться структурировать свой код по-другому. Во-вторых, в Scala есть все виды вложений, которые вы, вероятно, даже не замечаете, поэтому, если вы можете что-то разорвать, вы, вероятно, будете удивлены тем, где закончился поток кода (особенно с замыканиями). В-третьих, большинство «циклов» Scala на самом деле не являются обычными циклами - это вызовы методов, которые имеют свой собственный цикл, или они являются рекурсией, которая на самом деле может быть или не быть циклом - и хотя они act петлеобразный, трудно придумать последовательный способ узнать, что должен делать «перерыв» и тому подобное. Поэтому, чтобы быть последовательным, разумнее всего не иметь «перерыва».

Примечание : Есть функциональные эквиваленты всех этих, где вы возвращаете значение sum, а не изменяете его на месте. Это более идиоматические Scala. Однако логика остается прежней. (return становится return x и т. д.).

348
Rex Kerr

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

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

например

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

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

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

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

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

Scala 2.8, однако, включает в себя способ сломать

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

6
Ham

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

var (i, sum) = (0, 0)
while (sum < 1000) {
  sum += i
  i += 1
}
5
pathikrit
// 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

5
user1836270

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

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

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

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

3
fresskoma

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

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

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
2
Viraj.Hadoop

Близко к вашему решению было бы это: 

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 (не очень хорошее выражение - лучшего я не нахожу). Условие меняется на противоположное, что довольно быстро для такого размера задачи - возможно, вы получите что-то с разрывом для больших циклов. 

String.reverse неявно преобразуется в RichString, поэтому я делаю 2 дополнительных реверса. :) Более математический подход может быть более элегантным. 

2
user unknown

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

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

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"

        }
      }
    }
  }
}
1
Nilesh Shinde

По иронии судьбы, разрыв Scala в scala.util.control.Breaks является исключением:

def break(): Nothing = { throw breakException }

Лучший совет: НЕ используйте перерыв, продолжайте и переходите! ИМО - это то же самое, плохая практика и злой источник всех видов проблем (и горячих дискуссий) и, наконец, «считается вредным». Блок кода структурирован, также в этом примере разрывы излишни . Наш Эдсгер В. Дейкстра † писал:

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

1
Epicurist

Я получил ситуацию, как код ниже

 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-библиотеку, и механизм заключается в том, что ctx.read генерирует исключение, когда он ничего не может найти .. Я попал в ситуацию, когда: я должен разорвать цикл, когда было сгенерировано исключение, но scala.util .control.Breaks.break, использующий Exception для разрыва цикла, и он был в блоке catch, таким образом, он был перехвачен.

Я получил неприятный способ решить эту проблему: сделать цикл в первый раз и получить счет реальной длины. и использовать его для второго цикла.

отрываться от Scala не очень хорошо, когда вы используете некоторые Java-библиотеки.

0
Lucas Liu

Я новичок в 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)
});
0
Mohamad Alallan
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 . Объявляя цикл как разрушаемый.

0
ParvathiRajan Natarajan