tags:

views:

334

answers:

5

I'm embarrassed that I'm asking this question, but here I go:

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))
}
+6  A: 

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
j==NULL is not a valid comparison in R (though perhaps it should be). you need is.null(j)
Ian Fellows
Thanks! Updated for posterity.
Dusty
A: 

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
y!=0 should be y==0. a == NULL should be is.null(a). next should be break.
Ian Fellows
+2  A: 

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
-1, using exceptions for control flow is really poor form (they're for *exceptional* conditions, not expected behavior).
Mark E
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
+3  A: 

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
I think it is worth putting some support behind the "don't do this" and "matter of educational interest" points. :)
geoffjentry
Yowser. I hereby add support for the "don't do this" point! =)
Ken Williams
+4  A: 

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