9

Suppose you have the following function foo. When I'm running a for loop, I'd like it to skip the remainder of foo when foo initially returns the value of 0. However, break doesn't work when it's inside a function.

As it's currently written, I get an error message, no loop to break from, jumping to top level.

Any suggestions?

foo <- function(x) {
    y <- x-2
    if (y==0) {break} # how do I tell the for loop to skip this
    z <- y + 100
    z
}


for (i in 1:3) {
    print(foo(i))
}
isomorphismes
  • 8,233
  • 9
  • 59
  • 70
andrewj
  • 2,965
  • 8
  • 36
  • 37
  • Thanks everyone for the comments. Looking back at this, I should have changed `break` to `next` since that would have been more in line with what I was looking for. – andrewj Mar 25 '10 at 17:31

5 Answers5

8

Admittedly my R knowledge is sparse and this is drycoded, but something like the following should work:

foo <- function(x) {
    y <- x-2
    if (y==0) {return(NULL)} # return NULL then check for it
    z <- y + 100
    z
}

for (i in 1:3) {
    j <- foo(i)
    if(is.null(j)) {break}
    print(j)
}

Edit: updated null check for posterity

Dusty
  • 2,283
  • 2
  • 20
  • 24
6

As a matter of coding practice, don't do this. Having a function that can only be used inside a particular loop is not a great idea. As a matter of educational interest, you can evaluate the 'break' in the parent environment.

foo <- function(x) {
    y <- x-2
    if (y==0) {eval.parent(parse(text="break"),1)} 
    z <- y + 100
    z
}



for (i in 0:3) {
    print(foo(i))
}
Ian Fellows
  • 17,228
  • 10
  • 49
  • 63
  • 4
    I think it is worth putting some support behind the "don't do this" and "matter of educational interest" points. :) – geoffjentry Mar 25 '10 at 14:29
  • Yowser. I hereby add support for the "don't do this" point! =) – Ken Williams Mar 25 '10 at 18:46
  • I almost lost faith I will be able to handle multiple error types in my highest function by providing arguments `on.error1 = expression(action_on_error1)`, `on.error2 =...` and so on. To be able later handle them by `tryCatch(lower_lev_fun(), error = function(e) eval(on.error1))`. In my case parent.frame(1) evolve to `eval.parent(parse(text='next'),7)`, huh. It seems that it is the only way to make error handling so generic. Thanks! – jangorecki Dec 19 '13 at 01:22
4

Are we allowed to be a little more creative? Could you recast your problem to take advantage of the following approach, where the operation is based on vectors?

x <- 1:3  
y <- x[x-2 < 0] - 2 + 100 # I'm leaving the "- 2" separate to highlight the parallel to your code  
y  

If, however, a deeper form underlies the question and we need to follow this pattern for now, perhaps tweak it just a bit...

foo <- function(x) {
  y <- x - 2
  if (y != 0) {
    z <- y + 100
    z
  } # else implicitly return value is NULL
}

for (i in 1:3) {
  if (is.numeric(result <- foo(i))) {
    print(result)
  } else {
    break 
  }
}
William Doane
  • 1,416
  • 12
  • 20
2

An alternative way is to throw an error and catch it with try, like so:

foo <- function(x) {
    y <- x-2
    if (y==0) {stop("y==0")} 
    z <- y + 100
    z
}

try(for (i in 0:5) {
       print(foo(i))
}, silent=TRUE)

## or use tryCatch:
for (i in 0:5) {
   bar <- tryCatch(foo(i),error=function(e) NA)
   if(is.na(bar)){ break } else { print(bar) }
}
Leo Alekseyev
  • 12,893
  • 5
  • 44
  • 44
  • 1
    -1, using exceptions for control flow is really poor form (they're for *exceptional* conditions, not expected behavior). – Mark Elliot Mar 25 '10 at 14:07
  • Disagree. In some languages it would even viewed as idiomatic behavior (e.g. python). Either way, the key is that some sort of flag needs to be sent from foo() whether it be a NULL, an exception, or whatever. – geoffjentry Mar 25 '10 at 14:28
0

I have no clue how r works but I found the question interesting because I could lookup a new language's syntax so excuse my answer if it is totally wrong :)

foo <- function(x) { 
    y <- x-2 
    if (y!=0) z <- NULL else z <- y + 100 
    z 
}


for (i in 1:3)
{ 
    a <- foo(i)
    if (a == NULL) {next}
    print(a)
}
Kelsey
  • 47,246
  • 16
  • 124
  • 162