【问题标题】:R warning message on recursive expression: If you fail, try, try again递归表达式上的 R 警告消息:如果失败,请尝试,再试一次
【发布时间】:2011-03-09 17:58:29
【问题描述】:

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

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() })

【问题讨论】:

    标签: r recursion try-catch callstack substitution


    【解决方案1】:

    我不确定我是否可以准确描述原因,但我已经隔离了问题并可以解决它。基本问题是递归:retry(.FUN, max.attempts-1) - 当递归调用调用 substitute(.FUN) 时,它会上升到调用堆栈的一层来确定 .FUN 的值是什么 - 它必须重新开始评估一个承诺(函数参数的延迟执行)升级。

    解决方法是只进行一次替换:

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

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

    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
    

    【讨论】:

    • 我会认为 .FUN 在您的版本中的递归执行将不起作用,因为此时 .FUN 已经被评估过?我会测试它...
    • 我认为你是对的,但与此同时我想通了。我认为我的 f 是一个更好的例子,因为有时它会出错,有时不会。运行几次以检查它是否符合您的预期。当您尝试用完但仍有错误时,我不确定您希望它返回什么。
    • 哦,我看到你的帖子底部有我的 f 等价物:/
    • 太棒了!谢谢!我感觉这与堆栈中的级别有关,因为我可以看到每次迭代中警告的数量都在增加。
    • 并且该修改还使得在另一个函数中使用retry 成为可能。见底部的附录。
    【解决方案2】:

    不确定为什么会收到警告...但如果使用 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
      }
    

    【讨论】:

    • 谢谢@nico;我主要是好奇是什么导致了替代警告。但你的版本完美运行!
    猜你喜欢
    • 1970-01-01
    • 2011-09-12
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-06-22
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多