Подтвердить что ты не робот

Расширенная обработка ошибок

Недавно я задал этот вопрос и, к счастью, был отмечен withRestarts(), который кажется мне довольно впечатляющим и мощным:-) Теперь я очень хочу понять ошибку R возможности обработки немного более подробно.

Актуальные вопросы

  • Какое рекомендуемое использование simpleCondition()? Никогда не использовал его раньше, но я думал, что это может быть полезно для разработки пользовательских ошибок и предупреждений, которые на самом деле являются "истинными" условиями. Может ли он использоваться для создания базы данных конкретных условий, для которых доступны специальные обработчики?
  • Есть ли способ "заморозить" определенное состояние рабочего пространства всего R и вернуться к нему, чтобы перезапустить вычисление в определенной точке? Я знаю save.image(), но AFAIU, это не сохраняет "состояние" пути поиска (search() или searchpaths()).

Для заинтересованных

Два примера кода

  • Иллюстрация моего текущего использования withRestarts в зависимости от этого сообщения в блоге
  • попытаться определить "пользовательское условие"

Я был бы признателен за любые комментарии/предложения о том, что делать лучше; -)

Пример 1

require("forecast")
autoArimaFailsafe <- function(
    x,
    warning=function(w, ...) {
        message("autoArimaFailsafe> warning:")
        message(w)
        invokeRestart("donothing")},
    error=function(e, ...) {
        message("autoArimaFailsafe> error:")
        message(e)
        invokeRestart("abort")}
) {
    withRestarts(
        out <- tryCatch(
           {
                expr <- expression(auto.arima(x=x))
                return(eval(expr))
           },
           warning=warning,
           error=error 
        ),
        donothing=function(...) {
            return(eval(expr))
        },
        abort=function(...) {
            message("aborting")
            return(NULL)
        }
    )    
}
data(AirPassengers)
autoArimaFailsafe(x=AirPassengers)
autoArimaFailsafe(x="a")

Пример 2

require("forecast")
autoArimaFailsafe <- function(
    x,
    warning=function(w, ...) {
        message("autoArimaFailsafe> warning")
        invokeRestart("donothing")},
    error=function(e, ...) {
        message("autoArimaFailsafe> error")
        invokeRestart("abort")},
    condition=function(cond, ...) {
        out <- NULL
        message(cond)
        condmsg     <- conditionMessage(c=cond)
        condclass   <- class(cond)
        if (any(class(cond) == "simpleWarning")) {
            out <- warning(w=cond)
        } else if (any(class(cond) == "simpleError")) {
            out <- error(e=cond)
        } else if (any(class(cond) == "simpleCondition")) {
            if (condmsg == "invalid class: character") {
                out <- invokeRestart("forcedefault")
            }
        }
        return(out)
    }
) {
    withRestarts(
        out <- tryCatch(
           {
                expr <- expression(auto.arima(x=x))
                if (class(x) == "character") {
                    expr <- signalCondition(
                        simpleCondition("invalid class: character", 
                            call=as.call(expr))
                    )
                }
                return(eval(expr))
           },
           condition=condition
        ),
        donothing=function(...) {return(eval(expr))},
        abort=function(...) {
            message("aborting")
            return(NULL)
        },
        forcedefault=function(...) {
            data(AirPassengers)
            expr <- expression(auto.arima(x=AirPassengers))
            return(eval(expr))
        } 
    )
}
autoArimaFailsafe(x=AirPassengers)
autoArimaFailsafe(x=NULL)
autoArimaFailsafe(x="a")
4b9b3361

Ответ 1

В этом сообщение ссылается на вдохновение для обработки состояния R.

Для 1., я думаю о simpleCondition как о том, как можно построить пользовательские условия, например.

 myCondition <-
    function(message, call=NULL, type=c("overflow", "underflow", "zero"))
{
    type <- match.arg(type)             # only allowed types past here
    class <- c(type, "my", "condition")
    structure(list(message = as.character(message), call = call), 
        class = class)
}

является конструктором для создания пользовательских условий

> myCondition("oops")
<overflow: oops>
> myCondition("oops", type="underflow")
<underflow: oops>

Эти условия могут использоваться в tryCatch или withCallingHandlers

xx <- tryCatch({
    signalCondition(myCondition("oops", type="underflow"))
}, underflow=function(e) {
    message("underflow: ", conditionMessage(e))
    NA # return value, assigned to xx
})

Это классы S3, поэтому они могут иметь линейную иерархию - bad и worse - оба подкласса error.

myError <-
    function(message, call=NULL, type=c("bad", "worse"))
{
    type <- match.arg(type)
    class <- c(type, "error", "condition")
    structure(list(message=as.character(message), call=call),
              class=class)
}

Можно также создать ошибку, которая расширяет класс "simpleError" S3 как cond <- simpleError("oops"); class(cond) = c("myerr", class(cond)

С tryCatch мы просто получаем доступ к одному обработчику, первый (в смысле, описанном на? tryCatch), чтобы соответствовать классу условий

tryCatch({
    stop(myError("oops", type="worse"))
}, bad = function(e) {
    message("bad error: ", conditionMessage(e))
}, worse = function(e) {
    message("worse error: ", conditionMessage(e))  # here where we end up
}, error=function(e) {
    message("error: ", conditionMessage(e))
})

С withCallingHandlers у нас есть возможность ударить несколько обработчиков, если мы не вызываем перезапуск

withCallingHandlers({
    stop(myError("oops", type="bad"))
}, bad = function(e) {                             # here...
    message("bad error: ", conditionMessage(e))
}, worse = function(e) {
    message("worse error: ", conditionMessage(e))
}, error=function(e) {                             # ...and here...
    message("error: ", conditionMessage(e))
})                                                 # ...and top-level 'error'

withCallingHandlers({
    x <- 1
    warning(myError("oops", type="bad"))
    "OK"
}, bad = function(e) {                     # here, but continue at the restart
    message("bad warning: ", conditionMessage(e))
    invokeRestart("muffleWarning")
}, worse = function(e) {
    message("worse warning: ", conditionMessage(e))
})

Я не уверен в вашем вопросе 2; Я думаю, что это та ситуация, что обработчики вызовов предназначены для адресации - весь кадр, в котором было вызвано условие, находится в ожидании продолжения, как только вы вызываете перезапуск.