109

I have a program that does some data analysis and is a few hundred lines long.

Very early on in the program, I want to do some quality control and if there is not enough data, I want the program to terminate and return to the R console. Otherwise, I want the rest of the code to execute.

I've tried break,browser, and quit and none of them stop the execution of the rest of the program (and quit stops the execution as well as completely quitting R, which is not something I want to happen). My last resort is creating an if-else statement as below:

 if(n < 500){}
 else{*insert rest of program here*}

but that seems like bad coding practice. Am I missing something?

zx8754
  • 52,746
  • 12
  • 114
  • 209
user2588829
  • 1,523
  • 3
  • 10
  • 20
  • 4
    `quit` most certainly stops execution of the rest of the program. Please provide a [reproducible example](http://stackoverflow.com/q/5963269/271616). – Joshua Ulrich Jul 24 '13 at 14:42
  • @JakeBurkhead -- is my code above (with an empty if statement) the best way to go, then? @Joshua Ulrich, `quit` exits all of R, but I want to return to the R console because the program needs to remain open for my purposes. – user2588829 Jul 24 '13 at 14:45
  • What do you mean by a programme? Do you mean you are running a function you wrote or are you sourcing in a script? – Gavin Simpson Jul 24 '13 at 14:52
  • if-else is probably the correct way to handle this. Exceptions are for situations that should not happen if everything is used correctly. If it's something that can happen and you know how to handle it, use normal control flow. – Matthew Jul 27 '17 at 15:02

9 Answers9

74

You could use the stopifnot() function if you want the program to produce an error:

foo <- function(x) {
    stopifnot(x > 500)
    # rest of program
}
Michael Malick
  • 1,838
  • 12
  • 6
  • +1!I guess The function `foo` should be called the beginning of the script and contain other validations control... – agstudy Jul 24 '13 at 15:05
  • 26
    `stopifnot` is handy but a crafted response using `if(x < 500) { stop("Not enough observations in 'x': n < 500")}` might be preferred. Also, if this is something for a batch job, handling the issue *without* throwing an error is useful. – Gavin Simpson Jul 24 '13 at 15:13
  • 5
    Stop trying to confuse the OP. What he wants is quit() or stop(), not stopifnot(). – stackoverflowuser2010 Feb 23 '14 at 21:01
  • 13
    @stackoverflowuser2010 He doesn't want `quit` (see question!) I don't even think `stop` of `stopifnot` is the best way to handle this; `stop` throws an error, the whole script will just abort. Whilst `stopifnot` (or `stop`) seems to be the Answer OP liked best, writing a function to exit cleanly, without error, is more beneficial in a wider range of situations. Having written lots of long-running scripts for large data analysis jobs, nothing is more annoying than functions that throw errors instead of handling the issue and returning cleanly. But clearly I don't know what I'm talking about... – Gavin Simpson Feb 24 '14 at 04:40
  • Can you please clarify your comment about throwing an error @GavinSimpson? When I try `stop("my message")` I get printed into the terminal `Error: "my message" Execution halted`. So this shows an error message output, but are you saying it does not "throw" an error? (ie it will not stop a batch job that has been set to abort if any of the scripts it calls throw errors). Thanks! (Right now I am calling the script with Rscript) – rrr Jul 30 '19 at 03:10
  • What you are doing when you call `stop` is anticipating and throwing an error - the error comes from your code/function not an error raised lower down the stack in the R functions you call from your function, or lower still from the compiled code those R functions call. So `stop` will very much stop a script from running, or at least to move prematurely to the next step in the script, depending on how the script is written and how the script was invoked. AFAIU, an error raised via `stop` will cause a script invoked by `Rscript` to error out and be terminated. – Gavin Simpson Aug 02 '19 at 16:31
  • @GavinSimpson is there a way to not return an error to the OS? I call the script from a makefile which aborts because the return value with stop is non-zero. – rpm Oct 28 '19 at 14:04
  • @GavinSimpson, quick followup. I used quit(save="no") which seems to be a good workaround for me. – rpm Oct 28 '19 at 14:07
  • stopifnot stops the current expression, like stop. The question was about something that stops the whole script. – Luke Feb 11 '22 at 10:37
20

Perhaps you just want to stop executing a long script at some point. ie. like you want to hard code an exit() in C or Python.

print("this is the last message")
stop()
print("you should not see this")
netskink
  • 4,033
  • 2
  • 34
  • 46
  • 2
    For this code I get the error message `Error in eval(expr, envir, enclos) :`. – jochen May 10 '17 at 09:35
  • 2
    Yes, execution does indeed stop. Coincidentally, if you replace `stop()` with `exit()` or `please.stop.now()`, the script also stops (only the error messages are of course different). – jochen May 10 '17 at 13:00
  • 2
    @jochen Adding a quoted phrase inside the `stop()` command can help distinguish this "error" from other messages. For example: `stop("Manual break inserted here")` might be more informative than `stop()` alone. – Omar Wasow Jan 07 '20 at 02:19
19

Edited. Thanks to @Droplet, who found a way to make this work without the .Internal(): Here is a way to implement an exit() command in R.

exit <- function() { invokeRestart("abort") }    

print("this is the last message")
exit()
print("you should not see this")

Only lightly tested, but when I run this, I see this is the last message and then the script aborts without any error message.

Below is the uglier version from my original answer.

exit <- function() {
  .Internal(.invokeRestart(list(NULL, NULL), NULL))
}
jochen
  • 3,728
  • 2
  • 39
  • 49
  • 1
    Downside is that it is not allowed for code in a CRAN package. So if you intend to you use in a package which you want to upload to CRAN, it will yield a warning in the `R CMD CHECK`. – MS Berends Oct 28 '17 at 14:30
  • 1
    Yes, this looks more like a system function. It might break if internal details of the interpreter are changed, so may be better a part of the R core rather than in a separate packate? I found this by following different paths through the R source code to see how I could end up at the correct place to exit the interpreter without an error message being emitted. There were not so many ways I found to get there; this is why I use `.invokeRestart` which then in turn seems to need the `.Internal`. – jochen Oct 28 '17 at 16:16
  • Oh yes, other than the CRAN policies, I think it’s a nice solution! Let me deliver you a +10 rep ;) – MS Berends Oct 28 '17 at 16:58
  • 1
    weird. I just tried this and last output line was [1] "you should not see this" R version 3.4.3 (2017-11-30) Platform: x86_64-pc-linux-gnu (64-bit) Running under: Red Hat Enterprise Linux Server release 6.10 (Santiago) – CodingMatters Aug 13 '18 at 07:25
  • 1
    @aspiringGuru I just tried and it still works for me. Did you run the commands as a script? If you run them one after another in the command line (_e.g._ using copy and paste), then of course `exit()` cannot prevent the next command (which hasn't been entered yet) from running ... – jochen Aug 13 '18 at 10:03
  • just realized I hadn't responded., can't remember now how I resolved it, but yes, likely as you described. – CodingMatters Sep 04 '18 at 08:15
  • As far as I can tell, there is not need to use an internal function since `invokeRestart()` exists in base and is exported. This also solves this issue with CRAN rejecting packages using `.Internal()` – Droplet May 28 '20 at 18:48
  • @Droplet Thanks, I hadn't spotted `invokeRestart()` before. Sadly, I don't think it works for this purpose, because the funny `list(NULL, NULL)` argument doesn't make it past the argument checks. Or am I overlooking anything? – jochen Jun 03 '20 at 21:58
  • 4
    I got it to work with `exit <- function() { invokeRestart("abort") }` – Droplet Jun 06 '20 at 11:30
15

Reverse your if-else construction:

if(n >= 500) {
  # do stuff
}
# no need for else
Thomas
  • 43,637
  • 12
  • 109
  • 140
10

Edit: Seems the OP is running a long script, in that case one only needs to wrap the part of the script after the quality control with

if (n >= 500) {

.... long running code here

}

If breaking out of a function, you'll probably just want return(), either explicitly or implicitly.

For example, an explicit double return

foo <- function(x) {
  if(x < 10) {
    return(NA)
  } else {
    xx <- seq_len(x)
    xx <- cumsum(xx)
  }
  xx ## return(xx) is implied here
}

> foo(5)
[1] 0
> foo(10)
 [1]  1  3  6 10 15 21 28 36 45 55

By return() being implied, I mean that the last line is as if you'd done return(xx), but it is slightly more efficient to leave off the call to return().

Some consider using multiple returns bad style; in long functions, keeping track of where the function exits can become difficult or error prone. Hence an alternative is to have a single return point, but change the return object using the if () else () clause. Such a modification to foo() would be

foo <- function(x) {
  ## out is NA or cumsum(xx) depending on x
  out <- if(x < 10) {
    NA
  } else {
    xx <- seq_len(x)
    cumsum(xx)
  }
  out ## return(out) is implied here
}

> foo(5)
[1] NA
> foo(10)
 [1]  1  3  6 10 15 21 28 36 45 55
Gavin Simpson
  • 170,508
  • 25
  • 396
  • 453
  • I thought of this as well, but it's not clear that OP's talking about breaking out of a function. – Thomas Jul 24 '13 at 14:48
  • Yeah, Thomas is right -- I'm not talking about breaking out of a function. – user2588829 Jul 24 '13 at 14:49
  • 1
    @user2588829 You'd be much better off putting that as a function in R rather than a 100+ line script. – Gavin Simpson Jul 24 '13 at 14:54
  • @GavinSimpson oh, I'm still new to R so I didn't know that. If I define it as a 100+ line function is that better practice? – user2588829 Jul 24 '13 at 15:00
  • 1
    @user2588829 Yes, far better. You control the arguments of the function so can pass in what is needed. Also, instead of sourcing the 100+ lines of code to run the analysis you just do `myFun(arg1, arg2, arg3)` etc. It is just a much better way of organising things. – Gavin Simpson Jul 24 '13 at 15:01
  • ok great, thanks so much. In that case, I will be able to use `stop` and `break`, it seems. – user2588829 Jul 24 '13 at 15:03
  • Not `break` as that if for `for ()`, `while ()`, and `repeat ()` loops. `stop`, `stopifnot` and my use of handling the error (in one way) would all work of course. You can then also use `try()` to catch errors. I would suggest that you don't use `stop` if you are going to be repeatedly calling the function in a long batch job. `stop` will error out the entire job. It is better to anticipate (as I did, and return something informative) or catch the errors (using `try` or `tryCatch`) to allow a function to keep going and thus the entire job to continue. – Gavin Simpson Jul 24 '13 at 15:09
  • Stop trying to confuse the OP. What he wants is quit() or stop(). – stackoverflowuser2010 Feb 23 '14 at 21:02
  • @stackoverflowuser2010 the OP does not want `quit()`. You've just added a syntactically dubious Answer the spirit of which is already covered in the other Answers. The OP even mentions that they **don't** want `quit()`. – Gavin Simpson Feb 23 '14 at 22:03
7

This is an old question but there is no a clean solution yet. This probably is not answering this specific question, but those looking for answers on 'how to gracefully exit from an R script' will probably land here. It seems that R developers forgot to implement an exit() function. Anyway, the trick I've found is:

continue <- TRUE

tryCatch({
     # You do something here that needs to exit gracefully without error.
     ...

     # We now say bye-bye         
     stop("exit")

}, error = function(e) {
    if (e$message != "exit") {
        # Your error message goes here. E.g.
        stop(e)
    }

    continue <<-FALSE
})

if (continue) {
     # Your code continues here
     ...
}

cat("done.\n")

Basically, you use a flag to indicate the continuation or not of a specified block of code. Then you use the stop() function to pass a customized message to the error handler of a tryCatch() function. If the error handler receives your message to exit gracefully, then it just ignores the error and set the continuation flag to FALSE.

user2641103
  • 704
  • 9
  • 25
2

Here:

if(n < 500)
{
    # quit()
    # or 
    # stop("this is some message")
}
else
{
    *insert rest of program here*
}

Both quit() and stop(message) will quit your script. If you are sourcing your script from the R command prompt, then quit() will exit from R as well.

sm925
  • 2,648
  • 1
  • 16
  • 28
stackoverflowuser2010
  • 38,621
  • 48
  • 169
  • 217
1

I had a similar issue: Exit the current function, but not wanted to finish the rest of the code. Finally I solved it by a for() loop that runs only once. Inside the for loop you can set several differenct conditions to leave the current loop (function).

  for (i in T) {
    print('hello')
    if (leave.condition) next
    print('good bye')
  }
SwissChris
  • 11
  • 1
0

You can use the pskill function in the R "tools" package to interrupt the current process and return to the console. Concretely, I have the following function defined in a startup file that I source at the beginning of each script. You can also copy it directly at the start of your code, however. Then insert halt() at any point in your code to stop script execution on the fly. This function works well on GNU/Linux and judging from the R documentation, it should also work on Windows (but I didn't check).

# halt: interrupts the current R process; a short iddle time prevents R from
# outputting further results before the SIGINT (= Ctrl-C) signal is received 
halt <- function(hint = "Process stopped.\n") {
    writeLines(hint)
    require(tools, quietly = TRUE)
    processId <- Sys.getpid() 
    pskill(processId, SIGINT)
    iddleTime <- 1.00
    Sys.sleep(iddleTime)
}
blalasaadri
  • 5,990
  • 5
  • 38
  • 58
  • 1
    >pskill(processId, SIGINT) closes the session and kicks user out of RStudio even. It is quite dangerous but functional.... – Espanta Dec 30 '15 at 07:36
  • Didn't know it would crash RStudio, but the same issue is discussed in: http://stackoverflow.com/questions/32820534/invoke-interrupt-from-r-code On linux though, my solution works fine. Its advantage over stopifnot is that the stopifnot() Error message does not show up. – François Tonneau Dec 31 '15 at 18:33
  • I checked on Windows and it behaves mad. Thanks anyway. I like the pskill. – Espanta Jan 01 '16 at 16:35