How do I break out of a loop in Scala?
Asked Answered
M

19

315

How do I break out a loop?

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

How do I turn nested for loops into tail recursion?

From Scala Talk at FOSDEM 2009 http://www.slideshare.net/Odersky/fosdem-2009-1013261 on the 22nd page:

Break and continue Scala does not have them. Why? They are a bit imperative; better use many smaller functions Issue how to interact with closures. They are not needed!

What is the explanation?

Manaker answered 30/4, 2010 at 6:34 Comment(3)
Your comparision needs a second equals-sign: if(product.toString == product.toString.reverse) or maybe an equals-Method-call.Gerbil
yeah, i missed that one when i was typing it inManaker
I know I'm resurrecting an old question but I would love to know what the purpose of this code is? I first though that you were trying to find the largest "palindrome" product possible with the given combinations of i and j. If this code runs to completion without breaking out of the loop, the result is 906609 but by breaking out of the loop early, the result is 90909 so breaking out of the loop is not making the code "more efficient" as it is altering the result.Droopy
M
402

You have three (or so) options to break out of loops.

Suppose you want to sum numbers until the total is greater than 1000. You try

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

except you want to stop when (sum > 1000).

What to do? There are several options.

(1a) Use some construct that includes a conditional that you test.

var sum = 0
(0 to 1000).iterator.takeWhile(_ => sum < 1000).foreach(i => sum+=i)

(warning--this depends on details of how the takeWhile test and the foreach are interleaved during evaluation, and probably shouldn't be used in practice!).

(1b) Use tail recursion instead of a for loop, taking advantage of how easy it is to write a new method in Scala:

var sum = 0
def addTo(i: Int, max: Int) {
  sum += i; if (sum < max) addTo(i+1,max)
}
addTo(0,1000)

(1c) Fall back to using a while loop

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

(2) In Scala 3.3+, use boundaries instead (for more info):

import scala.util.boundary, boundary.break
boundary {
  for i <- 0 to 1000 do
    sum += i
    if sum >= 1000 then break()
}

(3) Throw an exception.

object AllDone extends Exception { }
var sum = 0
try {
  for (i <- 0 to 1000) { sum += i; if (sum>=1000) throw AllDone }
} catch {
  case AllDone =>
}

(3a) In Scala 2.8+ this is already pre-packaged in scala.util.control.Breaks using syntax that looks a lot like your familiar old break from C/Java:

import scala.util.control.Breaks._
var sum = 0
breakable { for (i <- 0 to 1000) {
  sum += i
  if (sum >= 1000) break
} }

(4) Put the code into a method and use return.

var sum = 0
def findSum { for (i <- 0 to 1000) { sum += i; if (sum>=1000) return } }
findSum

This is intentionally made not-too-easy for at least three reasons I can think of. First, in large code blocks, it's easy to overlook "continue" and "break" statements, or to think you're breaking out of more or less than you really are, or to need to break two loops which you can't do easily anyway--so the standard usage, while handy, has its problems, and thus you should try to structure your code a different way. Second, Scala has all sorts of nestings that you probably don't even notice, so if you could break out of things, you'd probably be surprised by where the code flow ended up (especially with closures). Third, most of Scala's "loops" aren't actually normal loops--they're method calls that have their own loop, or they are recursion which may or may not actually be a loop--and although they act looplike, it's hard to come up with a consistent way to know what "break" and the like should do. So, to be consistent, the wiser thing to do is not to have a "break" at all.

Note: There are functional equivalents of all of these where you return the value of sum rather than mutate it in place. These are more idiomatic Scala. However, the logic remains the same. (return becomes return x, etc.).

Monomolecular answered 30/4, 2010 at 7:29 Comment(28)
There's one more. Make it a method, and return explicitly.Spectacular
@Daniel: Um, doesn't (3) cover that?Monomolecular
I was thinking of return sum, not just return, but I guess it does.Spectacular
@rex kerr, 1a is slow, because memory for the stream is initialized, especially if this is in an inner loop 1b is what i was thinking of, but i see that i will need two tail recursions, and that's not good for clarity at all 2 and 3 are slower than direct loopsManaker
You can use .iterator (.elements in 2.7) instead of .toStream to avoid saving things in memory. I was giving examples of each, not necessarily specifying the fastest for your exact case. What are your benchmarks for (2) and (3)? How much slower are they?Monomolecular
To be frank, I didn't do any benchmarks. But I would like to point out, any overhead in an inner loop should be eliminatedManaker
@TiansHUo: Indeed it should. Do you realize how much overhead there is if you write for (i <- 1 to 1000000) { ... } instead of using a while loop? And a while loop is a special case of (1a)--just check some other condition to break early.Monomolecular
@Rex Kerr, actually I don't, why the overhead for a for-loop?Manaker
@TiansHUo: Because for is translated into a foreach call (possibly with other calls along the way), and that's all entirely generic, which is not as fast as incrementing a primitive counter. If you really want code to be fast, use var i=0; while (i<1000) { i+=1; ... }. Except in inner loops, though, there's no particular reason to be fast, so it's better to use the more powerful / flexible / convenient approach.Monomolecular
@kerr, what?! How did you know this?Manaker
@TiansHUo: I read Programming In Scala by Odersky et al, where the for loop is described, and I benchmarked various loops to see how they performed.Monomolecular
This is somewhat related to this question: stackoverflow.com/questions/3770989/… .Hollyhock
Re exceptions, although strictly true that you can throw an exception, this is arguably an abuse of the exception mechanism (see Effective Java). Exceptions are really to indicate situations that are truly unexpected and/or require a drastic escape from the code, i.e. errors of some kind. Quite apart from that, they certainly used to be pretty slow (not sure about the current situation) because there's little reason for JVMs to optimize them.Somaliland
@Somaliland - Exceptions are only slow if you need to compute a stack trace--notice how I created a static exception to throw instead of generating one on the fly! And they're a perfectly valid control construct; they're used in multiple places throughout the Scala library, since they're really the only way you can return through multiple methods (which if you have a pile of closures is something you sometimes need to do).Monomolecular
For me the best solution is tail recursion, so neat!Lopsided
In the recursive addTo method it seems cleaner to put sum as a parameter and have the method return an Int rather than Unit, so you don't have to use mutable vars. Number (4) for the list could be to replace the for-comprehensions with equivalent while-loops with appropriate stopping conditions.Cromorne
@macias - Use breakable, then.Monomolecular
@Rex Kerr, no-go, if I am not mistaken it is based on exceptions. I think on-fly inner function is better approach.Synonymize
@macias - What is wrong with using exceptions as part of your control flow mechanism? Each mechanism has its own strengths and weaknesses.Monomolecular
@Rex Kerr, you are pointing out weaknesses of the break construct (I don't agree with them), but then you suggest using exceptions for normal workflow! Exiting a loop is not an exceptional case, it is part of the algorithm, it is not the case of writing to non-existing file (for example). So in short suggested "cure" is worse than the "illness" itself. And when I consider throwing a real exception in breakable section... and all those hoops just to avoid evil break, hmm ;-) You have to admit, life is ironic.Synonymize
@macias - Sorry, my mistake. The JVM is using Throwables for control flow. Better? Just because they're typically used to support exception handling does not mean they can only be used for exception handling. Returning to a defined location from within a closure is just like throwing an exception in terms of control flow. No surprise, then, that this is the mechanism that is used.Monomolecular
@Rex Kerr, I'll stop here because it is more and more like a personal chat (now I would have to question if Java and JVM is something with proven design quality). I am not convinced at all, but until we find a better place to discuss this, let's stop OK?Synonymize
@RexKerr Well, for what it's worth you convinced me. Normally I'd be one to rail against Exceptions for normal program flow, but the two main reasons don't apply here. Those are: (1) they're slow [not when used in this way], and (2) they suggest exceptional behavior to someone reading your code [not if your library lets you call them break] If it look's like a break and it performs like a break, as far as I'm concerned it's a break.Think
Do I understand correctly that return inside a for body is compiled to throw new SomeException and for construct is put inside a try-catch? Because I don't see another way how could this be implemented.Mowbray
I've found the answer myself - my understanding was correct. In place of return this exception is thrown: scala.runtime.NonLocalReturnControlMowbray
Of these options, only 1a and 1b are substantially different to 'break'. Example 2 uses an exception in exactly the same way as 'break' would be used, only with additional boilerplate to define the exception class. Example 3 uses 'return' in exactly the same way that 'break' would be used, only with additional boilerplate to define a closure. If it's easy to overlook 'break' or 'continue' in the middle of a loop, how come the words 'return' and 'throw' are somehow magically different?Freedwoman
I think for procedure programming, use while loop is betterGuanine
(1c) has a typo: should be sum += i, not sum += 1Pepsin
C
77

This has changed in Scala 2.8 which has a mechanism for using breaks. You can now do the following:

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
        }
    }
}
Crabtree answered 28/2, 2011 at 5:7 Comment(7)
Does this use exceptions under the hood?Sacristan
This is using Scala as a procedural language ignoring the functional programming advantages (i.e. tail recursion). Not pretty.Lopsided
Mike: Yes, Scala is throwing an exception to break out of the loop. Galder: This answers the posted question "How do I break out of a loop in Scala?". Whether it's 'pretty' or not isn't relevant.Crabtree
@hohonuuli, so it is in try-catch block it won't break, right?Synonymize
@macias, that's right. If you surround the 'break' statement with a try/catch it won't break out of the loop. (I did test that). For the curious, the source code for Breaks is at scala-lang.org/api/current/index.html#scala.util.control.BreaksCrabtree
You can put that in the categories of "leaky abstractions". A bit sad, but that's the price to pay for running on a VM that was not written to support scala's constructs Then again, you're not supposed to catch all throwables. The recommended way to write a proper "catch all" handler is to use NonFatal, which not coincidently will not catch NonLocalReturnControl (the exception used internally when doing a return from inside an anonymous function) nor BreakControl (the one used to implement the break construct)Spigot
@Galder Zamarreño Why is tail recursion an advantage in this case? Isn't it simply an optimization (who's application is hidden for the newcomer and confusingly applied for the experienced). Is there any benefit for tail recursion in this example?Countdown
L
47

It is never a good idea to break out of a for-loop. If you are using a for-loop it means that you know how many times you want to iterate. Use a while-loop with 2 conditions.

for example

var done = false
while (i <= length && !done) {
  if (sum > 1000) {
     done = true
  }
}
Lewert answered 1/8, 2014 at 23:16 Comment(2)
This is what I feel is the right way to break out of loops in Scala. Is there anything wrong with this answer? (considering the low number of upvotes).Hollyhock
indeed simple and more readable. even the breakable -- break thingy is correct, it looks ugly and has problems with in inner try-catch. Although your solution doesn't work with a foreach, I will vote you up, honoring the simplicity.Wilser
O
16

To add Rex Kerr answer another way:

  • (1c) You can also use a guard in your loop:

     var sum = 0
     for (i <- 0 to 1000 ; if sum<1000) sum += i
    
Oatmeal answered 30/4, 2010 at 7:56 Comment(3)
I didn't include this as an option because it doesn't actually break the loop--it runs through all of it, but the if statement fails on every iteration after the sum is high enough, so it only does one if-statement's worth of work each time. Unfortunately, depending on how you've written the loop, that could be a lot of work.Monomolecular
@RexKerr: Wouldn't the compiler optimize it out anyway? Won't it be optimized out if not during first run then during JIT.Harslet
@MaciejPiechotka - The JIT compiler generally doesn't contain sufficiently sophisticated logic to recognize that an if-statement on a changing variable will always (in this particular special situation) return false and thus can be omitted.Monomolecular
A
8

Simply We can do in scala is

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

output :

scala> TestBreak.main(Array())
1
2
3
4
5
Alexis answered 20/7, 2018 at 11:56 Comment(1)
``` warning: Auto-application to () is deprecated. Supply the empty argument list () explicitly to invoke method break, or remove the empty argument list from its definition (Java-defined methods are exempt). In Scala 3, an unapplied method like this will be eta-expanded into a function. ```Mitchell
C
7

Since there is no break in Scala yet, you could try to solve this problem with using a return-statement. Therefore you need to put your inner loop into a function, otherwise the return would skip the whole loop.

Scala 2.8 however includes a way to break

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

Catalonia answered 30/4, 2010 at 6:35 Comment(5)
sorry, but I only wanted to break out the inner loop. You aren't implying that I should put it in a function?Manaker
Sorry, should have clarified that. Sure using a return means that you need to encapsulate the loop in a function. I've edited my answer.Catalonia
That's not nice at all. It seems that Scala doesn't like nested loops.Manaker
There doesn't seem to be a different way. You might want to take a look at this: scala-lang.org/node/257Catalonia
@TiansHUo: Why do you say that Scala doesn't like nested loops? You have the same issues if you are trying to break out of a single loop.Monomolecular
W
7

An approach that generates the values over a range as we iterate, up to a breaking condition, instead of generating first a whole range and then iterating over it, using Iterator, (inspired in @RexKerr use of Stream)

var sum = 0
for ( i <- Iterator.from(1).takeWhile( _ => sum < 1000) ) sum += i
Wooton answered 12/2, 2015 at 10:17 Comment(1)
yes, i like it. no breakable-excuse, I think it looks nicer.Exploration
B
5
// 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;
   }
}

use Break module http://www.tutorialspoint.com/scala/scala_break_statement.htm

Ballistic answered 10/3, 2014 at 4:37 Comment(0)
A
5

Just use a while loop:

var (i, sum) = (0, 0)
while (sum < 1000) {
  sum += i
  i += 1
}
Auricle answered 21/4, 2014 at 22:38 Comment(0)
H
4

Here is a tail recursive version. Compared to the for-comprehensions it is a bit cryptic, admittedly, but I'd say its functional :)

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

As you can see, the tr function is the counterpart of the outer for-comprehensions, and tr1 of the inner one. You're welcome if you know a way to optimize my version.

Hialeah answered 6/6, 2011 at 0:20 Comment(0)
G
2

Close to your solution would be this:

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)

The j-iteration is made without a new scope, and the product-generation as well as the condition are done in the for-statement (not a good expression - I don't find a better one). The condition is reversed which is pretty fast for that problem size - maybe you gain something with a break for larger loops.

String.reverse implicitly converts to RichString, which is why I do 2 extra reverses. :) A more mathematical approach might be more elegant.

Gerbil answered 18/5, 2010 at 6:58 Comment(0)
M
2

I am new to Scala, but how about this to avoid throwing exceptions and repeating methods:

object awhile {
def apply(condition: () => Boolean, action: () => breakwhen): Unit = {
    while (condition()) {
        action() match {
            case breakwhen(true)    => return ;
            case _                  => { };
        }
    }
}
case class breakwhen(break:Boolean);

use it like this:

var i = 0
awhile(() => i < 20, () => {
    i = i + 1
    breakwhen(i == 5)
});
println(i)

if you don’t want to break:

awhile(() => i < 20, () => {
    i = i + 1
    breakwhen(false)
});
Mulch answered 21/1, 2016 at 9:19 Comment(0)
D
2

The third-party breakable package is one possible alternative

https://github.com/erikerlandson/breakable

Example code:

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))
Danettedaney answered 5/3, 2017 at 19:58 Comment(1)
``` error: object manyangled is not a member of package com import com.manyangled.breakable._ ^ main.scala:35: error: not found: value break ```Mitchell
C
2
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.
   }
}

Basic method to break the loop, using Breaks class. By declaring the loop as breakable.

Cassaundra answered 10/5, 2018 at 10:22 Comment(0)
J
1

Ironically the Scala break in scala.util.control.Breaks is an exception:

def break(): Nothing = { throw breakException }

The best advice is: DO NOT use break, continue and goto! IMO they are the same, bad practice and an evil source of all kind of problems (and hot discussions) and finally "considered be harmful". Code block structured, also in this example breaks are superfluous. Our Edsger W. Dijkstra† wrote:

The quality of programmers is a decreasing function of the density of go to statements in the programs they produce.

Judgemade answered 23/6, 2014 at 8:0 Comment(0)
E
1

I got a situation like the code below

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

I am using a java lib and the mechanism is that ctx.read throw a Exception when it can find nothing. I was trapped in the situation that :I have to break the loop when a Exception was thrown, but scala.util.control.Breaks.break using Exception to break the loop ,and it was in the catch block thus it was caught.

I got ugly way to solve this: do the loop for the first time and get the count of the real length. and use it for the second loop.

take out break from Scala is not that good,when you are using some java libs.

Ervin answered 8/10, 2015 at 10:23 Comment(0)
C
1

Clever use of find method for collection will do the trick for you.

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)
Carmel answered 26/2, 2016 at 19:27 Comment(0)
K
1

Below is code to break a loop in a simple way

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"

        }
      }
    }
  }
}
Kirsten answered 16/12, 2018 at 12:50 Comment(0)
U
1

I don't know how much Scala style has changed in the past 9 years, but I found it interesting that most of the existing answers use vars, or hard to read recursion. The key to exiting early is to use a lazy collection to generate your possible candidates, then check for the condition separately. To generate the products:

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

Then to find the first palindrome from that view without generating every combination:

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

To find the largest palindrome (although the laziness doesn't buy you much because you have to check the entire list anyway):

palindromes.max

Your original code is actually checking for the first palindrome that is larger than a subsequent product, which is the same as checking for the first palindrome except in a weird boundary condition which I don't think you intended. The products are not strictly monotonically decreasing. For example, 998*998 is greater than 999*997, but appears much later in the loops.

Anyway, the advantage of the separated lazy generation and condition check is you write it pretty much like it is using the entire list, but it only generates as much as you need. You sort of get the best of both worlds.

Ulna answered 15/8, 2019 at 21:23 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.