递归表达式的R警告消息:如果失败,请尝试再试一次

Sha*_*ane 5 recursion callstack r try-catch substitution

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

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"))
Run Code Online (Sandbox Code Playgroud)

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

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

retry({ tmp <- function() { if(rnorm(1) < 0) stop("I'm here") else "success" }; tmp() })
Run Code Online (Sandbox Code Playgroud)

had*_*ley 8

我不确定我是否能完全描述原因,但我已经解决了问题并且可以解决它.基本问题是递归:retry(.FUN, max.attempts-1) - 当递归调用调用substitute(.FUN)它将调高堆栈的某个级别以确定其值.FUN是什么时- 它必须重新开始对promise的评估(函数参数的延迟执行)升级.

修复就是只进行一次替换:

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())
Run Code Online (Sandbox Code Playgroud)

要创建可以灵活使用的功能,我强烈建议尽量减少替代品的使用.根据我的经验,你通常最好有一个功能进行替换,另一个功能完成所有工作.这使得从另一个函数调用时可以使用该函数:

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
Run Code Online (Sandbox Code Playgroud)