I want to create a function that will retry an expression if it fails. Here's my working version:
retry <- function(.FUN, max.attempts=3, sleep.seconds=1) {
x <- NULL
if(max.attempts > 0) {
f <- substitute(.FUN)
x <- try(eval(f))
if(class(x) == "try-error") {
Sys.sleep(sleep.seconds)
return(suppressWarnings(retry(.FUN, max.attempts-1)))
}
}
x
}
retry(stop("I'm here"))
If I remove the suppressWarnings() function above, then I get a set of warnings on each recursive call. Does anyone know what I'm doing wrong that would cause that?
Here's an example that can be run repeatedly:
retry({ tmp <- function() { if(rnorm(1) < 0) stop("I'm here") else "success" }; tmp() })
I'm not sure if I can describe the cause exactly, but I've isolated the problem and can fix it. The basic problem is the recursion: retry(.FUN, max.attempts-1) - when the recursive call calls substitute(.FUN) it's going to have go up a level of the call stack to figure out what the value of .FUN is - it has to restart the evaluation of a promise (the delayed execution of function arguments) a level up.
A fix is to just do the substitution once:
retry <- function(.FUN, max.attempts = 3, sleep.seconds = 0.5) {
expr <- substitute(.FUN)
retry_expr(expr, max.attempts, sleep.seconds)
}
retry_expr <- function(expr, max.attempts = 3, sleep.seconds = 0.5) {
x <- try(eval(expr))
if(inherits(x, "try-error") && max.attempts > 0) {
Sys.sleep(sleep.seconds)
return(retry_expr(expr, max.attempts - 1))
}
x
}
f <- function() {
x <- runif(1)
if (x < 0.5) stop("Error!") else x
}
retry(f())
To create functions that you can use flexibly, I highly recommend minimising the use of substitute. In my experience, you're usually best off having one function that does the substitution, and another that does all the work. This makes it possible to use the function when called from another function:
g1 <- function(fun) {
message("Function starts")
x <- retry(fun)
message("Function ends")
x
}
g1(f())
# Function starts
# Error in eval(expr, envir, enclos) : object 'fun' not found
# Error in eval(expr, envir, enclos) : object 'fun' not found
# Error in eval(expr, envir, enclos) : object 'fun' not found
# Error in eval(expr, envir, enclos) : object 'fun' not found
# Function ends
g2 <- function(fun) {
message("Function starts")
expr <- substitute(fun)
x <- retry_expr(expr)
message("Function ends")
x
}
g2(f())
# Function starts
# Error in f() : Error!
# Function ends
# [1] 0.8079241
Not sure about why you get the warnings... but if use a for loop they disappear.
retry <- function(.FUN, max.attempts=3, sleep.seconds=1)
{
x <- NULL
for (i in 1:max.attempts)
{
f <- substitute(.FUN)
x <- try(eval(f))
if (class(x) == "try-error")
{
Sys.sleep(sleep.seconds)
}
else
{
return (x)
}
}
x
}
Related
I am keep getting an error for this small problem below:
assignment5<- read.csv(file="C:/Users/Marjolein/Desktop/assignment5data.csv",header=TRUE,se p=";")
d <- as.vector(assignment5[["demand"]])
x<-400
n <- 1461
IFunction <- function (j,d,x){
if (d[j] <= x)
{
I <- 1
} else
{
I <- 0
}
return(I)
}
for (j in 1:(n)){
I[j] <- IFunction(j,d,x)
I
}
The error is: Error in I[j] <- IFunction(j, d, x) :
object of type 'closure' is not subsettable
So I guess there is something wrong with; I[j] <- IFunction(j,d,x). since now it sees I as a function, but it should be seen as a value
Is there someone who can help me?
With kind regards,
Marjolein straathof
I is a function. Pick another variable name.
You can test this by typing the variable names, and seeing what happens:
> I
function (x)
{
structure(x, class = unique(c("AsIs", oldClass(x))))
}
> J
Error: object 'J' not found
That 'object not found' is good news for you: it's not being used by anything else. You'll need to let R know what you want to do with it before you start using it like a vector. NA is a good choice to initialize something:
J <- NA
for (j in 1:(n)){
J[j] <- IFunction(j,d,x)
J
}
I have very little understanding of how environments work in R.
Here is my code
push = function(l, x)
{
assign(l, append(eval(as.name(l)), x), envir=parent.frame())
}
main = function()
{
mylist = list("hello")
push("mylist","World")
}
main()
This code returns the error
Error in eval(expr, envir, enclos) : object 'mylist' not found
Why does it return this error?
How to fix that?
The eval is not taking place with respect to the parent frame of the push call, only the assign is.
One could pass the parent.frame() to eval or adopt the following style which seems clearer. (main is unchanged.)
push = function(l, x, envir = parent.frame())
{
envir[[l]] <- append(envir[[l]], x)
}
or pass the objects themselves and return them to avoid dealing with environments:
push <- function(l, x) append(l, x)
main <- function() {
mylist <- list("Hello")
push(mylist, "World")
}
main()
I am trying to figure out how to pinpoint the line where an error is thrown when using foreach and doParallel. Here's an example:
foreach.example <- function()
{
require("doParallel")
z <- foreach (i = 1:2) %do%
{
x <- i + 'a'
}
return(z)
}
So within the %do% there is a bug where i am adding a character to a numeric. (Everything I say here also applies to %dopar%,). When I run this I get:
> foreach.example()
Error in { (from test_foreach.R#3) : task 1 failed - "non-numeric argument to binary operator"
I can't tell from this where in the loop I had the error line 3 is the foreach line, not the offending line. When I run debugger() I get:
> debugger()
Message: Error in { (from test_foreach.R#3) : task 1 failed - "non-numeric argument to binary operator"
Available environments had calls:
1: foreach.example()
2: test_foreach.R#3: foreach(i = 1:2) %do% {
x <- i + "a"
}
3: e$fun(obj, substitute(ex), parent.frame(), e$data)
4: stop(simpleError(msg, call = expr))
Note that frame 2 will generally indicate the whole loop so I can't find the line where the error was actually thrown.
If instead I run this without the foreach, I get useful information:
regular.example <- function()
{
z <- list()
for (i in 1:2) {
x <- i + 'a'
z <- c(z, list(x))
}
return(z)
}
>regular.example()
Error in i + "a" (from test_foreach.R#12) : non-numeric argument to binary operator
and the debugger takes me to the line in code that threw the exception.
Any ideas on how to identify the line number with the excpetion when using foreach? Thanks.
Try modifying your foreach call to include .verbose = TRUE
z <- foreach (i = 1:2, .verbose = T) %do% ...
Consider:
guy <- new.env(FALSE)
guy$stuff <- mean
guy$lib <- library
guy$stole_this_data <- mtcars
ls(guy)
How can I evaluate an expression within an environment inside a function?
For instance I can do with(guy, args(stuff)) to the below and return:
> with(guy, args(stuff))
function (x, ...)
NULL
But within a functon:
foo <- function(env, fun) {
with(env, args(fun))
}
foo(guy, stuff)
## > foo(guy, stuff)
## Error in args(fun) : could not find function "stuff"
Try this:
> foo <- function(env, fun) eval(substitute(args(fun)), env)
> foo(guy, stuff)
function (x, ...)
NULL
ADDED. Regarding the comment below here is an example where zz is not in env or its ancestors (but is in foo2 and in f, the caller of foo2) and it does give a not found error as the comment wished:
> foo2 <- function(env, fun, zz = 1) eval(substitute(fun), env)
> f <- function() { zz <- 100; foo2(guy, zz+1) }
> f()
Error in eval(expr, envir, enclos) : object 'zz' not found
If you want to continue to use the with construct, this is an alternative:
foo <- function(env, fun) {
fun <- substitute(fun)
eval(bquote(with(env, {
.(fun)
})))
}
myFunc <- function(x)
{
x <- timeSeries(x, charvec=as.Date(index(x)))
t<-tryCatch( doSomething(x), error=function(x) rep(0,ncol(x))
)
t
}
How do I pass x into the error function? When I run the above I get:
Error in rep(0, ncol(x)) : invalid 'times' argument
The error argument is a handler, documented (see ?tryCatch) to accept one argument (the error condition). The error handler has access to whatever variables were available at the time stop was invoked. So
f = function() {
tryCatch({
i = 1
stop("oops")
}, error=function(e) {
stop(conditionMessage(e), " when 'i' was ", i)
})
}
catches the error thrown by the code, discovers the value i, and emits a more informative message. So I'd guess
myFunc <- function(x)
{
tryCatch({
x <- timeSeries(x, charvec=as.Date(index(x)))
doSomething(x)
}, error=function(...) rep(0, ncol(x)))
}