2010-06-29 25 views
5

Tôi muốn tạo một hàm sẽ thử lại một biểu thức nếu nó không thành công. Dưới đây là phiên bản làm việc của tôi:Thông báo cảnh báo R trên biểu thức đệ quy: Nếu bạn thất bại, hãy thử, thử lại

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

Nếu tôi loại bỏ các suppressWarnings() chức năng trên, sau đó tôi nhận được một tập hợp các cảnh báo trên mỗi cuộc gọi đệ quy. Có ai biết tôi đang làm sai điều gì không?

Dưới đây là một ví dụ mà có thể chạy liên tục:

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

Trả lời

6

Tôi không chắc liệu mình có thể mô tả chính xác nguyên nhân hay không, nhưng tôi đã phân lập được sự cố và có thể khắc phục sự cố. Vấn đề cơ bản là đệ quy: retry(.FUN, max.attempts-1) - khi cuộc gọi đệ quy gọi substitute(.FUN) nó sẽ tăng lên một mức của ngăn xếp cuộc gọi để tìm ra giá trị của .FUN là - nó phải khởi động lại việc đánh giá lời hứa (thực thi chậm trễ của các đối số hàm) lên cấp.

Một sửa chữa là chỉ cần làm việc thay thế một lần:

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

Để tạo các chức năng mà bạn có thể sử dụng linh hoạt, tôi khuyên bạn nên giảm thiểu việc sử dụng thay thế. Theo kinh nghiệm của tôi, bạn thường tốt nhất có một chức năng thay thế, và một chức năng khác làm tất cả công việc. Điều này giúp bạn có thể sử dụng chức năng khi được gọi từ một chức năng khác:

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

Tôi đã nghĩ rằng việc thực thi đệ quy của .UN trong phiên bản của bạn sẽ không hoạt động vì .FUN đã được đánh giá tại thời điểm đó? Tôi sẽ kiểm tra nó ... – Shane

+0

Tôi nghĩ rằng bạn đã đúng, nhưng trong khi chờ đợi tôi đã tìm ra. Tôi nghĩ rằng f của tôi là một ví dụ tốt hơn bởi vì đôi khi nó lỗi và đôi khi nó không. Chạy nó một vài lần để kiểm tra xem nó có làm những gì bạn mong đợi không. Tôi không chắc chắn những gì bạn muốn nó trở lại khi bạn hết nỗ lực nhưng vẫn có một lỗi. – hadley

+0

Ồ, tôi thấy bạn có số điểm tương đương với số f của tôi ở cuối bài đăng của bạn:/ – hadley

3

Không chắc về lý do tại sao bạn nhận được những lời cảnh báo ... nhưng nếu sử dụng một vòng lặp for chúng biến mất.

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

Cảm ơn @nico; Tôi đã hầu như tò mò về những gì đã gây ra các cảnh báo với thay thế. Nhưng phiên bản của bạn hoạt động hoàn hảo! – Shane

Các vấn đề liên quan