R: stop a function within another function - r

I am working on a code and ran into a problem I can't seem to fix. Basically the idea is that I have one overall function and within that function are several other functions. My problem is: within one of those function I need the option to stop if a certain condition is not satisfied, but using stopifnot() of if (...) {stop} stops the overall functions as well. Is there a way in which I can stop the specific function from running without also stopping the overall function?
For example:
full=function(){
message("before")
x=2
small=function(x){
if (x<3){stop("smaller than 3")
print(x)
}
}
small(x)
message("after")
}
full()
What I want to do here is quit the "small" function if x is smaller than 3 (so x is not printed), but I still want to run the message "after". Is there a way to do that?
Thanks in advance.

Perhaps you are just looking for the return() function. Otherwise,
try error handling facilities:
full <- function() {
message("before")
small <- function(x){
if (x<3) {
# or just e.g. return(NULL)
stop("smaller than 3")
}
print(x)
}
tryCatch({
small(2)
}, error=function(err) {
# do nothing on error
})
message("after")
}
full()
## before
## after

Related

Skip Error and Continue Function in R

I have a data set with p number of variables. I want a function that creates histograms of each variable, and when it encounters a problem it attempts to create a barplot instead. If it encounters a problem after attempting the barplot it skips that p, and continues to the next p.
What I'm thinking (pseudocode):
for (i in ncol(data)) {
try( hist(data[i])) {
if "error" try( barplot(data[i])) {
if "error" print ("Error") }
}
continue to i # code executes through all columns of data
}
}
I've tried using the try() and tryCatch() based on other stackoverflow posts, but I can't seem to figure out how to work it.
You probably want to use tryCatch for this. Something like the following should do the trick (though I can't test it since you don't provide any data).
for(i in 1:ncol(d)) {
tryCatch(hist(d[[i]], main=i), error=function(e) {
tryCatch(barplot(d[[i]], main=i), error=function(e) {
print('Error')
})
})
}

Multiple expectations with test_that

Is there a way to have multiple for expections for an expect_that unit test? For instance, for a given expect_that() statement, I'd like to expect that the function f() gives a warning and also returns the number 10.
test_that("f works as expected", {
expect_warning(f())
expect_equal(f(), 10)
}
)
If I understand your context correctly, this should work. The test would fail and report if either or both of the expectations weren't met.
To run the function only once, you could try wrapping the function within the test_that:
test_that("f works as expected", {
a <- tryCatch(f(), warning=function(w) return(list(f(), w)))
expect_equal(a[[2]], "warning text")
expect_equal(a[[1]], 10)
rm(a)
}
)
I haven't tested this so I'm not sure if it'll work in your particular case, but I've used similar approaches with test_that in the past.
context("Checking blah")
test_that("blah works",{
f <- function(){warning("blah"); return(10)}
expect_warning(x <- f())
expect_equal(x, 10)
})
You can save the output while checking for the warning. After that check that the output is what you expect.

Stopping an R script quietly and return control to the terminal [duplicate]

Is there any way to stop an R program without error?
For example I have a big source, defining several functions and after it there are some calls to the functions. It happens that I edit some function, and want the function definitions to be updated in R environment, but they are not actually called.
I defined a variable justUpdate and when it is TRUE want to stop the program just after function definitions.
ReadInput <- function(...) ...
Analyze <- function(...) ...
WriteOutput <- function(...) ...
if (justUpdate)
stop()
# main body
x <- ReadInput()
y <- Analyze(x)
WriteOutput(y)
I have called stop() function, but the problem is that it prints an error message.
ctrl+c is another option, but I want to stop the source in specific line.
The problem with q() or quit() is that it terminates R session, but I would like to have the R session still open.
As #JoshuaUlrich proposed browser() can be another option, but still not perfect, because the source terminates in a new environment (i.e. the R prompt will change to Browser[1]> rather than >). Still we can press Q to quit it, but I am looking for the straightforward way.
Another option is to use if (! justUpdate) { main body } but it's clearing the problem, not solving it.
Is there any better option?
I found a rather neat solution here. The trick is to turn off all error messages just before calling stop(). The function on.exit() is used to make sure that error messages are turned on again afterwards. The function looks like this:
stop_quietly <- function() {
opt <- options(show.error.messages = FALSE)
on.exit(options(opt))
stop()
}
The first line turns off error messages and stores the old setting to the variable opt. After this line, any error that occurs will not output a message and therfore, also stop() will not cause any message to be printed.
According to the R help,
on.exit records the expression given as its argument as needing to be executed when the current function exits.
The current function is stop_quietly() and it exits when stop() is called. So the last thing that the program does is call options(opt) which will set show.error.messages to the value it had, before stop_quietly() was called (presumably, but not necessarily, TRUE).
There is a nice solution in a mailing list here that defines a stopQuietly function that basically hides the error shown from the stop function:
stopQuietly <- function(...) {
blankMsg <- sprintf("\r%s\r", paste(rep(" ", getOption("width")-1L), collapse=" "));
stop(simpleError(blankMsg));
} # stopQuietly()
> stopQuietly()
I have a similar problem and, based on #VangelisTasoulas answer, I got a simple solution.
Inside functions, I have to check if DB is updated. If it is not, stop the execution.
r=readline(prompt="Is DB updated?(y/n)")
Is DB updated?(y/n)n
if(r != 'y') stop('\r Update DB')
Update DB
Just putting \r in the beginning of the message, overwrite Error: in the message.
You're looking for the function browser.
You can use the following solution to stop an R program without error:
if (justUpdate)
return(cat(".. Your Message .. "))
Just return something at the line you want to quit the function:
f <- function(x, dry=F) {
message("hi1")
if (dry) return(x)
message("hi2")
x <- 2*x
}
y1 <- f(2) # = 4 hi1 hi2
y2 <- f(2, dry=T) # = 2 hi1
In addition to answer from Stibu on Mar 22 '17 at 7:29, if you want to write a message as a part of stop(), this message is not written.
I perceive strange that following two lines have to be used meaning on.exit(options(options(show....))) doesn't work.
opt <- options(show.error.messages = F)
on.exit(options(opt))
I had forgotten the answer to this and needed to look it up and landed here... You posted the hint to the answer in your question...
ctrl+c is another option, but I want to stop the source in specific line.
Signal an error, warning, or message
rlang::inform("Updated Only")
rlang::interrupt()
I've found it good to write a script and run it with source(). In the script, a write exit statements as a special class of error that a tryCatch() can pick up and send back as just a message:
exit <- function(..., .cl = NULL) {
# Use to capture acceptable stop
cond <- structure(
list(.makeMessage(...), .cl),
class = c("exitError", "error", "condition"),
names = c("message", "call")
)
stop(cond)
}
foo <- function() {
exit("quit here")
1
}
tryCatch(
# rather than foo(), you might use source(filename)
foo(),
exitError = function(e) message(e$message)
)
#> quit here
Created on 2022-01-24 by the reprex package (v2.0.1)
You can use with_options() in the withr package to temporarily disable error messages and then you can call stop() directly.
Here is an example:
weird_math <- function(x, y, z) {
if (x > z) {
withr::with_options(
list(show.error.messages = FALSE),
{
print("You can execute other code here if you want")
stop()
}
)
}
# only runs if x <= z
x + y ^ z
}
weird_math(1, 2, 3)
[1] 9
weird_math(3, 2, 1)
[1] "You can execute other code here if you want"
why not just use an if () {} else {}? It's only a couple of characters...
f1 <- function(){}
f2 <- function(){}
if (justUpdate) {
} else {
# main body
}
or even
f1 <- function(){}
f2 <- function(){}
if (!justUpdate) {
# main body
}
The below code work for me stopped without error messages.
opt <- options(show.error.messages = FALSE)
on.exit(options(opt))
break

Stop an R program without error

Is there any way to stop an R program without error?
For example I have a big source, defining several functions and after it there are some calls to the functions. It happens that I edit some function, and want the function definitions to be updated in R environment, but they are not actually called.
I defined a variable justUpdate and when it is TRUE want to stop the program just after function definitions.
ReadInput <- function(...) ...
Analyze <- function(...) ...
WriteOutput <- function(...) ...
if (justUpdate)
stop()
# main body
x <- ReadInput()
y <- Analyze(x)
WriteOutput(y)
I have called stop() function, but the problem is that it prints an error message.
ctrl+c is another option, but I want to stop the source in specific line.
The problem with q() or quit() is that it terminates R session, but I would like to have the R session still open.
As #JoshuaUlrich proposed browser() can be another option, but still not perfect, because the source terminates in a new environment (i.e. the R prompt will change to Browser[1]> rather than >). Still we can press Q to quit it, but I am looking for the straightforward way.
Another option is to use if (! justUpdate) { main body } but it's clearing the problem, not solving it.
Is there any better option?
I found a rather neat solution here. The trick is to turn off all error messages just before calling stop(). The function on.exit() is used to make sure that error messages are turned on again afterwards. The function looks like this:
stop_quietly <- function() {
opt <- options(show.error.messages = FALSE)
on.exit(options(opt))
stop()
}
The first line turns off error messages and stores the old setting to the variable opt. After this line, any error that occurs will not output a message and therfore, also stop() will not cause any message to be printed.
According to the R help,
on.exit records the expression given as its argument as needing to be executed when the current function exits.
The current function is stop_quietly() and it exits when stop() is called. So the last thing that the program does is call options(opt) which will set show.error.messages to the value it had, before stop_quietly() was called (presumably, but not necessarily, TRUE).
There is a nice solution in a mailing list here that defines a stopQuietly function that basically hides the error shown from the stop function:
stopQuietly <- function(...) {
blankMsg <- sprintf("\r%s\r", paste(rep(" ", getOption("width")-1L), collapse=" "));
stop(simpleError(blankMsg));
} # stopQuietly()
> stopQuietly()
I have a similar problem and, based on #VangelisTasoulas answer, I got a simple solution.
Inside functions, I have to check if DB is updated. If it is not, stop the execution.
r=readline(prompt="Is DB updated?(y/n)")
Is DB updated?(y/n)n
if(r != 'y') stop('\r Update DB')
Update DB
Just putting \r in the beginning of the message, overwrite Error: in the message.
You're looking for the function browser.
You can use the following solution to stop an R program without error:
if (justUpdate)
return(cat(".. Your Message .. "))
Just return something at the line you want to quit the function:
f <- function(x, dry=F) {
message("hi1")
if (dry) return(x)
message("hi2")
x <- 2*x
}
y1 <- f(2) # = 4 hi1 hi2
y2 <- f(2, dry=T) # = 2 hi1
In addition to answer from Stibu on Mar 22 '17 at 7:29, if you want to write a message as a part of stop(), this message is not written.
I perceive strange that following two lines have to be used meaning on.exit(options(options(show....))) doesn't work.
opt <- options(show.error.messages = F)
on.exit(options(opt))
I had forgotten the answer to this and needed to look it up and landed here... You posted the hint to the answer in your question...
ctrl+c is another option, but I want to stop the source in specific line.
Signal an error, warning, or message
rlang::inform("Updated Only")
rlang::interrupt()
I've found it good to write a script and run it with source(). In the script, a write exit statements as a special class of error that a tryCatch() can pick up and send back as just a message:
exit <- function(..., .cl = NULL) {
# Use to capture acceptable stop
cond <- structure(
list(.makeMessage(...), .cl),
class = c("exitError", "error", "condition"),
names = c("message", "call")
)
stop(cond)
}
foo <- function() {
exit("quit here")
1
}
tryCatch(
# rather than foo(), you might use source(filename)
foo(),
exitError = function(e) message(e$message)
)
#> quit here
Created on 2022-01-24 by the reprex package (v2.0.1)
You can use with_options() in the withr package to temporarily disable error messages and then you can call stop() directly.
Here is an example:
weird_math <- function(x, y, z) {
if (x > z) {
withr::with_options(
list(show.error.messages = FALSE),
{
print("You can execute other code here if you want")
stop()
}
)
}
# only runs if x <= z
x + y ^ z
}
weird_math(1, 2, 3)
[1] 9
weird_math(3, 2, 1)
[1] "You can execute other code here if you want"
why not just use an if () {} else {}? It's only a couple of characters...
f1 <- function(){}
f2 <- function(){}
if (justUpdate) {
} else {
# main body
}
or even
f1 <- function(){}
f2 <- function(){}
if (!justUpdate) {
# main body
}
The below code work for me stopped without error messages.
opt <- options(show.error.messages = FALSE)
on.exit(options(opt))
break

Why does R say no loop for break/next, jumping to top level

Why does R throw the error "Error in value[3L] : no loop for break/next, jumping to top level" instead of going to the next iteration of a loop? I'm on R version 2.13.1 (2011-07-08)
for (i in seq(10)) {
tryCatch(stop(), finally=print('whoops'), error=function(e) next)
}
This problem came up because I wanted to create a different image or no image at all when plot failed. The code, using joran's approach, would look like this:
for (i in c(1,2,Inf)) {
fname = paste(sep='', 'f', i, '.png')
png(fname, width=1024, height=768)
rs <- tryCatch(plot(i), error=function(e) NULL)
if (is.null(rs)){
print("I'll create a different picture because of the error.")
}
else{
print(paste('image', fname, 'created'))
dev.off()
next
}
}
Maybe you could try :
for (i in seq(10)) {
flag <- TRUE
tryCatch(stop(), finally=print('whoops'), error=function(e) flag<<-FALSE)
if (!flag) next
}
Unfortunately, once you get inside your error function you're no longer in a loop. There's a way you could hack around this:
for (i in seq(10)) {
delayedAssign("do.next", {next})
tryCatch(stop(), finally=print('whoops'),
error=function(e) force(do.next))
}
Though that is... well, hacky. Perhaps there is a less hacky way, but I don't see one right off.
(This works because delayedAssign happens every loop, canceling out the efforts of force)
EDIT
Or you could use continuations:
for (i in seq(10)) {
callCC(function(do.next) {
tryCatch(stop(), finally=print('whoops'),
error=function(e) do.next(NULL))
# Rest of loop goes here
print("Rest of loop")
})
}
EDIT
As Joris points out, you probably shouldn't actually use either of these, because they're confusing to read. But if you really want to call next in a loop, this is how :).
Wouldn't it make more sense to put the next outside the tryCatch based on an if check? Something like this:
for (i in c(1,2,Inf)) {
rs <- tryCatch(seq(i), finally=print('whoops'), error=function(e) NULL)
if (is.null(rs)){
print("I found an error!")
}
else{
next
}
}
although I'm not sure this is what you want, since I'm a little unclear on what you're trying to do.
EDIT
Based on the OP's revisions, this formulation works for me:
plotFn <- function(fname,i){
png(fname, width=400, height=200)
plot(i)
dev.off()
}
for (i in c(1,Inf,3)) {
fname = paste('f', i, '.png',sep="")
rs <- tryCatch(plotFn(fname,i), error=function(e){dev.off(); return(NULL)})
if (is.null(rs)){
print("I'll create a different picture because of the error.")
}
else{
print(paste('image', fname, 'created'))
next
}
}
I'm certain that not having a dev.off() call in the case of an error needed to be fixed. I'd have to dig a little deeper to figure out exactly why separating png and plot was causing problems. But I think it's probably cleaner to keep the png(); plot(); dev.off() sequence self contained anyway. Also note that I put a dev.off() in the error function.
I haven't tested what will happen if plotFn throws an error on png(), never creates the device and then reaches the error function and calls dev.off(). Behavior may depend on what else you have going on in your R session.

Resources