5

我想创建一个函数,如果它失败将重试表达式。这是我的工作版本:

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"))

如果我删除suppressWarnings()上面的函数,那么每次递归调用都会收到一组警告。有谁知道我做错了什么会导致这种情况?

这是一个可以重复运行的示例:

retry({ tmp <- function() { if(rnorm(1) < 0) stop("I'm here") else "success" }; tmp() })
4

2 回答 2

8

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
于 2010-06-30T17:26:00.483 回答
4

不知道为什么你会收到警告......但如果使用for循环它们就会消失。

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
  }
于 2010-06-29T17:27:14.497 回答