2010-06-29 10 views
5

Ich möchte eine Funktion erstellen, die einen Ausdruck erneut versuchen wird, wenn es fehlschlägt. Hier ist meine Arbeitsversion:R-Warnmeldung auf rekursiven Ausdruck: Wenn Sie fehlschlagen, versuchen Sie es erneut versuchen

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

Wenn ich die suppressWarnings() Funktion oben entfernen, dann habe ich eine Reihe von Warnungen auf jedem rekursiven Aufruf erhalten. Weiß jemand, was ich falsch mache, das würde das verursachen?

Hier ist ein Beispiel, die wiederholt ausgeführt werden können:

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

Antwort

6

Ich bin nicht sicher, ob ich die Ursache genau beschreiben kann, aber ich habe das Problem isoliert und kann es beheben. Das Grundproblem ist die Rekursion: retry(.FUN, max.attempts-1) - wenn der rekursive Aufruf substitute(.FUN) aufruft, wird es eine Ebene des Aufruf-Stacks gehen, um herauszufinden, was der Wert .FUN ist - es muss die Bewertung eines Versprechens (die verzögerte Ausführung) neu starten von Funktionsargumenten) eine Ebene höher.

Ein Fix ist nur einmal die Auswechslung tun:

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

Funktionen erstellen, die Sie flexibel nutzen können, empfehle ich die Verwendung von Ersatz zu minimieren. Meiner Erfahrung nach ist es normalerweise am besten, wenn Sie eine Funktion haben, die die Substitution durchführt, und eine andere, die die ganze Arbeit erledigt. Dies ermöglicht es, die Funktion zu verwenden, wenn sie von einer anderen Funktion aufgerufen wird:

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

Ich hätte gedacht, dass die rekursive Ausführung von .FUN in Ihrer Version nicht funktionieren würde, weil .FUN an diesem Punkt bereits ausgewertet worden wäre? Ich werde es testen ... – Shane

+0

Ich denke, du hast Recht, aber in der Zwischenzeit habe ich es herausgefunden. Ich denke, mein f ist ein besseres Beispiel, weil es manchmal Fehler macht und manchmal nicht. Führen Sie es ein paar Mal aus, um zu überprüfen, ob es Ihren Erwartungen entspricht. Ich bin nicht sicher, was Sie wollten, dass es zurückkehrt, wenn Sie keine Versuche mehr haben, aber immer noch einen Fehler haben. – hadley

+0

Oh, ich sehe, Sie hatten ein Äquivalent zu meinem f in der Unterseite Ihres Beitrags:/ – hadley

3

Nicht sicher, warum Sie die Warnungen ... aber wenn eine for Schleife verschwinden sie verwenden.

+0

Dank @nico; Ich war hauptsächlich neugierig, was die Warnungen mit Ersatz verursacht hat. Aber deine Version funktioniert perfekt! – Shane

Verwandte Themen