views:

614

answers:

4

For Problem 4 of Project Euler 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
 }
}

And does anyone know how to turn nested for loops into tail recursion?

EDIT: from Scala Talk at FOSDEM 2009 http://www.slideshare.net/Odersky/fosdem-2009-1013261
On the 22th 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!

Any explanations?

+2  A: 

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 will include the break-statement.

Ham
sorry, but I only wanted to break out the inner loop. You aren't implying that I should put it in a function?
TiansHUo
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.
Ham
That's not nice at all. It seems that Scala doesn't like nested loops.
TiansHUo
There doesn't seem to be a different way. You might want to take a look at this: http://www.scala-lang.org/node/257
Ham
@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.
Rex Kerr
+18  A: 

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).toStream.takeWhile(sum < 1000).foreach(i => sum+=i)

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

(2) 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 =>
}

(3) 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.

(Incidentally, Scala 2.8 gets around this by having easy syntax that looks a lot like standard breaks in loops, except you have to specify what you want broken by adding a breakable { ... }, and it actually works by throwing exceptions.)

Rex Kerr
There's one more. Make it a method, and `return` explicitly.
Daniel
@Daniel: Um, doesn't (3) cover that?
Rex Kerr
I was thinking of `return sum`, not just `return`, but I guess it does.
Daniel
@rex kerr,1a is slow, because memory for the stream is initialized, especially if this is in an inner loop1b is what i was thinking of, but i see that i will need two tail recursions, and that's not good for clarity at all2 and 3 are slower than direct loops
TiansHUo
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?
Rex Kerr
To be frank, I didn't do any benchmarks. But I would like to point out, any overhead in an inner loop should be eliminated
TiansHUo
@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.
Rex Kerr
@Rex Kerr, actually I don't, why the overhead for a for-loop?
TiansHUo
@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.
Rex Kerr
@kerr, what?! How did you know this?
TiansHUo
@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.
Rex Kerr
+2  A: 

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
    
Patrick
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.
Rex Kerr
A: 

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.

user unknown