19 异常处理
R中的条件(condition)系统提供了一组成对的工具,提示函数正在发生异常情况,并允许该函数的用户处理它
19.1 signals conditions
19.1.1 error
停止执行并返回到顶部
19.1.2 warning
捕获警告并聚合显示
要使警告立即显示,请设置 options(warn = 1)
要将警告转换为错误,请设置
options(warn = 2)
。这通常是 调试警告的最简单方法,因为一旦出现错误,就可以 使用工具,例如查找源头traceback()
使用
options(warn = 0)
恢复默认行为。
19.1.3 message
消息立即显示
参数quiet = TRUE
抑制所有消息suppressMessages()
19.2 忽略信息
-
忽略错误:
try()
,最好是使用tryCatch()
-
忽略警告。
suppressWarnings()
Show the code
suppressWarnings({ warning("Uhoh!") warning("Another warning") 1 }) #> [1] 1
-
忽略消息。
suppressMessages()
Show the code
suppressMessages({ message("Hello there") "ABC" }) #> [1] "ABC"
19.3 条件处理程序
19.3.1 条件对象
Show the code
cnd <- rlang::catch_cnd(stop("An error"))
str(cnd)
#> List of 2
#> $ message: chr "An error"
#> $ call : language force(expr)
#> - attr(*, "class")= chr [1:3] "simpleError" "error" "condition"
conditionMessage(cnd)
#> [1] "An error"
conditionCall(cnd)
#> force(expr)
19.3.2 退出处理程序
处理条件信息
tryCatch()
定义exitinghandlers,,通常用于处理错误情况。它允许您覆盖默认的错误行为。
Show the code
tryCatch(
error = function(cnd) {
# code to run when error is thrown
},
code_to_run_while_handlers_are_active
)
在发出条件信号后,控制权将传递给处理程序,并且永远不会返回到原始代码,这实际上意味着代码退出
类似Python的
try:
{}
finally:
{}
Show the code
tryCatch(
{
write.table("Hi!", "data/异常处理.csv")
},
finally = {
# always run
x <- read.table("data/异常处理.csv")
print("你好")
}
)
#> [1] "你好"
19.3.3 调用处理程序
withCallingHandlers()
定义callinghandlers,适合处理非错误条件,一旦处理程序返回,代码执行将正常继续
Show the code
withCallingHandlers(
warning = function(cnd) {
# code to run when warning is signalled
},
message = function(cnd) {
# code to run when message is signalled
},
code_to_run_while_handlers_are_active
)
Show the code
tryCatch(
message = function(cnd) cat("Caught a message!\n"),
{
message("Someone there?")
message("Why, yes!")
}
)
#> Caught a message!
withCallingHandlers(
message = function(cnd) cat("Caught a message!\n"),
{
message("Someone there?")
message("Why, yes!")
}
)
#> Caught a message!
#> Caught a message!
19.4 自定义条件
Show the code
my_log(letters)
#> Error in `my_log()`:
#> ! `x` must be a numeric vector; not character.
my_log(1:10, base = letters)
#> Error in `my_log()`:
#> ! `base` must be a numeric vector; not character.
Show the code
abort_bad_argument <- function(arg, must, not = NULL) {
msg <- glue::glue("`{arg}` must {must}")
if (!is.null(not)) {
not <- typeof(not)
msg <- glue::glue("{msg}; not {not}.")
}
abort("error_bad_argument",
message = msg,
arg = arg,
must = must,
not = not
)
}
stop_custom <- function(.subclass, message, call = NULL, ...) {
err <- structure(
list(
message = message,
call = call,
...
),
class = c(.subclass, "error", "condition")
)
stop(err)
}
err <- catch_cnd(
stop_custom("error_new", "This is a custom error", x = 10)
)
class(err)
#> [1] "error_new" "error" "condition"
err$x
#> [1] 10
重写my_log
Show the code
my_log <- function(x, base = exp(1)) {
if (!is.numeric(x)) {
abort_bad_argument("x", must = "be numeric", not = x)
}
if (!is.numeric(base)) {
abort_bad_argument("base", must = "be numeric", not = base)
}
base::log(x, base = base)
}
Show the code
my_log(letters)
#> Error in `abort_bad_argument()`:
#> ! `x` must be numeric; not character.
my_log(letters)
#> Error in `abort_bad_argument()`:
#> ! `x` must be numeric; not character.
19.5 Debugging
traceback()
: 函数调用栈
表示第7次调用函数出现错误。
debug()
: 标记函数,调用函数时出现错误自动进入browser,输入 n
一行一行运行直到出现错误
Show the code
debug(lm)
lm(y ~ x)
#> debugging in: lm(y ~ x)
#> debug: {
#> ret.x <- x
#> ret.y <- y
#> cl <- match.call()
#> mf <- match.call(expand.dots = FALSE)
#> m <- match(c("formula", "data", "subset", "weights", "na.action",
#> "offset"), names(mf), 0L)
#> mf <- mf[c(1L, m)]
#> mf$drop.unused.levels <- TRUE
#> mf[[1L]] <- quote(stats::model.frame)
#> mf <- eval(mf, parent.frame())
#> if (method == "model.frame")
#> return(mf)
#> else if (method != "qr")
#> warning(gettextf("method = '%s' is not supported. Using 'qr'",
#> method), domain = NA)
#> mt <- attr(mf, "terms")
#> y <- model.response(mf, "numeric")
#> w <- as.vector(model.weights(mf))
#> if (!is.null(w) && !is.numeric(w))
#> stop("'weights' must be a numeric vector")
#> offset <- model.offset(mf)
#> mlm <- is.matrix(y)
#> ny <- if (mlm)
#> nrow(y)
#> else length(y)
#> if (!is.null(offset)) {
#> if (!mlm)
#> offset <- as.vector(offset)
#> if (NROW(offset) != ny)
#> stop(gettextf("number of offsets is %d, should equal %d (number of observations)",
#> NROW(offset), ny), domain = NA)
#> }
#> if (is.empty.model(mt)) {
#> x <- NULL
#> z <- list(coefficients = if (mlm) matrix(NA_real_, 0,
#> ncol(y)) else numeric(), residuals = y, fitted.values = 0 *
#> y, weights = w, rank = 0L, df.residual = if (!is.null(w)) sum(w !=
#> 0) else ny)
#> if (!is.null(offset)) {
#> z$fitted.values <- offset
#> z$residuals <- y - offset
#> }
#> }
#> else {
#> x <- model.matrix(mt, mf, contrasts)
#> z <- if (is.null(w))
#> lm.fit(x, y, offset = offset, singular.ok = singular.ok,
#> ...)
#> else lm.wfit(x, y, w, offset = offset, singular.ok = singular.ok,
#> ...)
#> }
#> class(z) <- c(if (mlm) "mlm", "lm")
#> z$na.action <- attr(mf, "na.action")
#> z$offset <- offset
#> z$contrasts <- attr(x, "contrasts")
#> z$xlevels <- .getXlevels(mt, mf)
#> z$call <- cl
#> z$terms <- mt
#> if (model)
#> z$model <- mf
#> if (ret.x)
#> z$x <- x
#> if (ret.y)
#> z$y <- y
#> if (!qr)
#> z$qr <- NULL
#> z
#> }
#> debug: ret.x <- x
#> debug: ret.y <- y
#> debug: cl <- match.call()
#> debug: mf <- match.call(expand.dots = FALSE)
#> debug: m <- match(c("formula", "data", "subset", "weights", "na.action",
#> "offset"), names(mf), 0L)
#> debug: mf <- mf[c(1L, m)]
#> debug: mf$drop.unused.levels <- TRUE
#> debug: mf[[1L]] <- quote(stats::model.frame)
#> debug: mf <- eval(mf, parent.frame())
#> Error in eval(predvars, data, env): object 'y' not found
# Browse[1]> n
# debug: ret.x <- x
# Browse[1]> n
# debug: ret.y <- y
# Browse[1]> n
# debug: cl <- match.call()
# Browse[1]> n
# debug: mf <- match.call(expand.dots = FALSE)
# Browse[1]> n
# debug: m <- match(c("formula", "data", "subset", "weights", "na.action",
# "offset"), names(mf), 0L)
# Browse[1]> n
# debug: mf <- mf[c(1L, m)]
# Browse[1]> n
# debug: mf$drop.unused.levels <- TRUE
# Browse[1]> n
# debug: mf[[1L]] <- quote(stats::model.frame)
# Browse[1]> n
# debug: mf <- eval(mf, parent.frame())
# Browse[1]> n
# Error in eval(predvars, data, env) : object 'y' not found
undebug(lm)