0

I have a complex condition (including side effects) for deciding if go next in a loop, so I would like to extract the logic in a function.

CRAN checks return the following warning:

Found the following significant warnings:
     Note: break used in wrong context: no loop is visible 
     Note: next used in wrong context: no loop is visible 

I have tried doing something like reported in the following minimal reprex, but it returns an error.

# comented code are not needed to reproduce the issue, they are 
# just a sample of some compelxity I would like to extract from the
# cycle using `b`
b <- function() {
  # cat("Doing something complex w/ side effect") 
  # complex_cond <- TRUE
  # if (complex_cond) {
    break
  # } else {
  #   cat("Perform compelx cycle computation")
  # }
}

res <- for (j in letters[1:2]) {
  cat(j)
  for (i in 1:2) {
    cat(i, "\n")
    b()
  }
}
#> a1
#> Error in b(): no loop for break/next, jumping to top level

expected <- for (j in letters[1:2]) {
  cat(j)
  for (i in 1:2) {
    cat(i, "\n")
    break
  }
}
#> a1 
#> b1

res |> identical(expected)
#> Error in identical(res, expected): object 'res' not found

Created on 2022-08-29 by the reprex package (v2.0.1)

Independently from the fact that it could be helpful or not, nor if it could be a good practice (surely not! :-) ), do you know if it is possible to do that or, if not, why?

Thank you! :-)

Jonas
  • 121,568
  • 97
  • 310
  • 388
Corrado
  • 625
  • 6
  • 12
  • 3
    Don't have your function do the `break`/`next` command, just have it evaluate the conditional and return TRUE or FALSE. `if(my_function()) break else next` or something like that. Also `break` is **not** a function, so `break()` is a syntax error, use `break` without the `()`. – Gregor Thomas Aug 29 '22 at 15:05
  • @r2evans if you want to write up an answer about call stack separation between the loop and the `break` not working, please go ahead! – Gregor Thomas Aug 29 '22 at 15:07
  • Thank you, @GregorThomas. I have fixed the break call (that, in this case, didn't return any syntax error, and it works perfectly well with the expected behavior, despite the fact I agree with you it was a typo and the correct syntax is the current one!...). I have also added some commented code to `b` just as an example of what I mean. – Corrado Aug 29 '22 at 15:25
  • 1
    But again, there's no need for the `break` to be in `b` (and I believe that syntactically you cannot do it). You can have `b` return TRUE of FALSE for break or no break, or you can have it return a meaningful string that you check and dispatch appropriately. I would suggest that *"Perform compelx cycle computation"* might be it's own function, but you don't have to do it that way if you don't want to. You just can't put the `break` **inside** `b()`. – Gregor Thomas Aug 29 '22 at 15:46
  • Thank you, I agree with your suggestions and best practices (extracting the function, testing the *complex cycle computation* and everything else: don't manage a loop from inside a function). Still, I am curious about a solution (that I have found!) and why I cannot do that (that I still haven't found yet). – Corrado Aug 29 '22 at 22:01

2 Answers2

0

Although you can't break out of a loop from within a function you can return early from an outer function from within an inner function using callCC. See R - evaluating return() in parent environment for an example.

However, here we are just concerned with a conditional break for debugging purposes and assuming you control the code I think it would be easier to just define a logical option which turns on or off debugging as shown below. Below we turn it on or use options(debug = NULL) or options(debug = FALSE) to turn it off.

  options(debug = TRUE)

  for (j in letters[1:2]) {
    cat(j)
    for (i in 1:2) {
      cat(i, "\n")
      if (getOption("debug", FALSE)) break
    }
  }
G. Grothendieck
  • 254,981
  • 17
  • 203
  • 341
0

[Edit 20220901: the "solution" reported here DOESN'T work!]

Thanks to the @g-grothendieck English rephrasing, I have found the solution here.

I still don't understand why I cannot do that directly. Anyway: that's it; there, you can find a solution.

Thank you all!

Corrado
  • 625
  • 6
  • 12
  • 1
    `eval.parent(parse(text = "break"))` does not work. If you try running the code in the linked-to post it results in: *Error in eval(expr, p) : no loop for break/next, jumping to top level* – G. Grothendieck Aug 30 '22 at 13:28
  • That's right. I won't cancel the answare to maintain evidence of my error. Thank you – Corrado Sep 01 '22 at 18:32