2010-06-29 27 views
5

Chcę utworzyć funkcję, która będzie ponawiać próbę wyrażenia, jeśli się nie powiedzie. Oto moja wersja robocza:Komunikat ostrzegawczy R w wyrażeniu rekursywnym: Jeśli nie, spróbuj, spróbuj ponownie

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

Jeśli usunąć funkcję suppressWarnings() powyżej, a następnie dostać zestaw ostrzeżenia na każdym rekurencyjnego wywołania. Czy ktoś wie, co robię źle, które by to spowodowało?

Oto przykład, który może być wielokrotnie uruchomić:

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

Odpowiedz

6

Nie jestem pewien, czy potrafię dokładnie opisać przyczynę, ale wyizolowałem problem i mogę to naprawić. Podstawowym problemem jest rekurencja: retry(.FUN, max.attempts-1) - kiedy wywołanie rekursywne wywoła substitute(.FUN), wzrośnie do poziomu stosu wywołań, aby dowiedzieć się, jaka jest wartość .FUN - musi ponownie uruchomić ocenę obietnicy (opóźnione wykonanie argumentów funkcji) wyższy poziom.

jest poprawka po prostu zrobić podstawienie jednorazowo:

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

Aby utworzyć funkcje, które można wykorzystać elastycznie, bardzo polecam minimalizując użycie zamiennika. Z mojego doświadczenia wynika, że ​​najlepiej jest mieć jedną funkcję, która zastępuje, i inną, która wykonuje całą pracę. Dzięki temu można korzystać z tej funkcji po wywołaniu z innej funkcji:

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

Myślałem, że rekursywne wykonanie .FUN w Twojej wersji nie będzie działać, ponieważ .FUN zostałaby już oceniona w tym punkcie? Sprawdzę to ... – Shane

+0

Myślę, że masz rację, ale w międzyczasie doszedłem do tego. Myślę, że moje f jest lepszym przykładem, ponieważ czasami to błędy, a czasem nie. Uruchom go kilka razy, aby sprawdzić, czy spełnia on Twoje oczekiwania. Nie jestem pewien, co chciałeś, aby powrócił, gdy skończy Ci się próba, ale nadal występuje błąd. – hadley

+0

O, widzę, że masz odpowiednik mojego f na dole twojego posta:/ – hadley

3

Nie jestem pewien dlaczego masz ostrzeżenia ... ale jeśli użyć for pętlę znikają.

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

Dzięki @nico; Byłem w większości ciekaw, co powoduje ostrzeżenia z substytutem. Ale twoja wersja działa idealnie! – Shane

Powiązane problemy