2010-06-29 39 views
5

我想創建一個函數,如果失敗,它將重試表達式。這是我的工作版本:關於遞歸表達式的R警告消息:如果失敗,請嘗試再試一次

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

回答

6

我不確定是否可以完全描述原因,但我已經隔離了這個問題並且可以解決它。基本問題是遞歸: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 
+0

我以爲在你的版本中遞歸執行.FUN是行不通的,因爲.FUN在那時已經被評估過了?我會測試它...... – Shane 2010-06-30 17:40:49

+0

我認爲你是對的,但在此期間,我明白了。我認爲我的f是一個更好的例子,因爲有時它是錯誤的,有時它不會。運行幾次,檢查它是否符合你的期望。我不確定你什麼時候想要它返回,當你嘗試用完,但仍然有錯誤。 – hadley 2010-06-30 17:42:59

+0

哦,我看到你在你的帖子底部有一個相當於我的f:/ – hadley 2010-06-30 17:43:36

3

不知道爲什麼你的警告......但如果使用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 
    } 
+0

謝謝@nico;我大部分都很好奇是什麼原因導致了替代警告。但是你的版本完美無缺! – Shane 2010-06-30 18:06:57

相關問題