10

Very simple example code (only for demonstration, no use at all):

repeat {
  while (1 > 0) {
    for (i in seq(1, 100)) {
      break # usually tied to a condition
    }
    break
  }
  break
}
print("finished")

I want to break out from multiple loops without using break in each loop separately. According to a similar question regarding python, wrapping my loops into a function seems to be a possible solution, i.e. using return() to break out of every loop in the function:

nestedLoop <- function() {
  repeat {
    while (1 > 0) {
      for (i in seq(1, 100)) {
        return()
      }
    }
  }
}

nestedLoop()
print("finished")

Are there other methods available in R? Maybe something like labeling loops and then specifying which loop to break (like in Java) ?

Community
  • 1
  • 1
Robomatix
  • 171
  • 1
  • 4
  • 12
  • 2
    I don't now other methods and would use `return` like this. But of course, I haven't written a nested R loop in months and haven't used an R `while` or `repeat` loop in years. – Roland Jun 01 '16 at 14:21
  • The problem that I have with wrapping my loops into a function is, that I'll possibly have to return large lists of variables from within the loops to get them to the global environment, if elsewhere needed in my code. – Robomatix Jun 01 '16 at 16:00
  • No, your problem is using so many loops. Efficient R code rarely uses nested loops. – Roland Jun 01 '16 at 16:07
  • We are not talking about taking performance to the limit, but about orders of magnitude in timings. – Roland Jun 01 '16 at 18:09
  • I think you would want to _return_ _something_. – IRTFM Jun 01 '16 at 18:42

2 Answers2

19

Using explicit flags, and breaking out of loops conditionally on those flags can give one more flexibility. Example:

stop = FALSE
for (i in c(1,2,3,4)){
    for (j in c(7,8,9)){
        print(i)
        print(j)
        if (i==3){
            stop = TRUE # Fire the flag, and break the inner loop
            break
        }
        }
    if (stop){break} # Break the outer loop when the flag is fired
    }

The above code will break the two nested loops when i=3. When the last line (if (stop){break}) is commented out, then only the inner loop gets broken at i=3, but the outer loop keeps running, i.e. it practically skips the cases of i=3. This structure is easy to play around with, and is as flexible as one may need.

FatihAkici
  • 4,679
  • 2
  • 31
  • 48
6

I think your method of wrapping your nested loops into a function is the cleanest and probably best approach. You can actually call return() in the global environment, but it will throw an error and looks ugly, like so:

for (i in 1:10) {
  for (a in 1:10) {
    for(b in 1:10) {

      if (i == 5 & a == 7 & b == 2) { return() }

    }
  }
}

print(i)
print(a)
print(b)

Which looks like this in the command line:

> for (i in 1:10) {
+   for (a in 1:10) {
+     for(b in 1:10) {
+       
+       if (i == 5 & a == 7 & b == 2) { return() }
+       
+     }
+   }
+ }
Error: no function to return from, jumping to top level
> 
> print(i)
[1] 5
> print(a)
[1] 7
> print(b)
[1] 2

Obviously far better and cleaner to use the function method.

EDIT:

Added an alternative solution to making the error look nicer given by Roland:

for (i in 1:10) {
  for (a in 1:10) {
    for(b in 1:10) {

      if (i == 5 & a == 7 & b == 2) { stop("Let's break out!") }

    }
  }
}

print(i)
print(a)
print(b)
giraffehere
  • 1,118
  • 7
  • 18
  • Well, you could just throw an error directly using `simpleError` instead. – Roland Jun 01 '16 at 14:37
  • Replacing `return()` with `simpleError("", call = return())` still throws the error in the console, though I haven't seen `simpleError` before so I might be using it incorrectly. – giraffehere Jun 01 '16 at 14:42
  • Try `stop("Let's break out!")` which returns a `simpleError` object. – Roland Jun 01 '16 at 14:47
  • Added in an alternative version, is that what you meant? – giraffehere Jun 01 '16 at 14:55
  • 1
    Yes, that's better than using an expression/function that throws an error only because you want an error. Of course, it is still not a good solution because this is not appropriate use of errors. You'll see why if you wrap this code in a function. You couldn't get a return value from that function. – Roland Jun 01 '16 at 14:57
  • Yeah, I agree that wrapping this in a function is the right way to do what OP wants. Just doing this for the sake of trying. – giraffehere Jun 01 '16 at 15:08