美文网首页数据科学与R语言
6. 调试、条件处理和防御式编程

6. 调试、条件处理和防御式编程

作者: kkkkkkang | 来源:发表于2020-04-17 17:04 被阅读0次

这章之前有章讲环境的,经常用到的概念和遇到的坑在前面或多或少都有涉及,所以跳过了。这章简写为debug

  • 追溯错误发生步骤,Rstudio自带功能/traceback()函数可实现
> f <- function(a) g(a)
> g <- function(b) h(b)
> h <- function(c) i(c)
> i <- function(d) "a" + d
> f(10)
#会报存在非数值参数的错误,点击Traceback,出现如下图的结果:从下往上看,错误发生在f()到i()逐渐调用,直至错误发生。点击Rerun with debug,可以运行到错误代码行
#如果没有使用Rstudio, traceback()函数照样可以实现
> traceback()
4: i(c) at #1
3: h(b) at #1
2: g(a) at #1
1: f(10)
  • 函数抛出意想不到的警告时,可以使用options(warn =2),讲警告转换为错误,然后使用常规调试工具解决
> f <- function(a){
+   if (a ==0){
+     warning("a can not equal to zero")
+   }
+   a
+ }
> f(0)
[1] 0
Warning message:
In f(0) : a can not equal to zero
> options(warn =2)
> f(0)
  • 消息转错误
> message2error <- function(code) {
+     withCallingHandlers(code, message = function(e) stop(e))
+ }
> 
> f <- function() g()
> g <- function() message("Hi!")
> g()
Hi!
> message2error(g())
 Error in message("Hi!") : Hi! 
> traceback()
10: stop(e) at #2
9: (function (e) 
   stop(e))(list(message = "Hi!\n", call = message("Hi!")))
8: signalCondition(cond)
7: doWithOneRestart(return(expr), restart)
6: withOneRestart(expr, restarts[[1L]])
5: withRestarts({
       signalCondition(cond)
       defaultHandler(cond)
   }, muffleMessage = function() NULL)
4: message("Hi!") at #1
3: g()
2: withCallingHandlers(code, message = function(e) stop(e)) at #2
1: message2error(g())
  • 条件处理
# try()
> f1 <- function(x) {
+     log(x)
+     10
+ }
> f1("x")
Error in log(x) : 数学函数中用了非数值参数
#遇到错误,抛出错误,然后并不继续执行
#try()可以忽略错误,继续执行下面的代码
> f2 <- function(x) {
+     try(log(x))
+     10
+ }
> f2("a")
Error in log(x) : 数学函数中用了非数值参数
[1] 10
#可以加上参数silent = TRUE来进制Error信息抛出
> f2 <- function(x) {
+   try(log(x),silent = TRUE)
+   10
+ }
> f2("a")
[1] 10
#try()中放置长代码,需要用{}
> try({
+     a <- 1
+     b <- "x"
+     a + b
+ })
Error in a + b : 二进列运算符中有非数值参数
  • 捕获try()的输出
success <- try(1 + 2)
failure <- try("a" + "b")
#> Error in "a" + "b" : non-numeric argument to binary operator
class(success)
#> [1] "numeric"
class(failure)
#> [1] "try-error"
  • 列表中多个元素使用同一个函数进行处理时,try()就很有用
elements <- list(1:10, c(-1, 10), c(TRUE, FALSE), letters)
results <- lapply(elements, log)
#> Warning in FUN(X[[i]], ...): NaNs produced
#> Error in FUN(X[[i]], ...): non-numeric argument to mathematical function
results <- lapply(elements, function(x) try(log(x)))
#> Warning in log(x): NaNs produced
#> Error in log(x) : non-numeric argument to mathematical function
  • 自定义可以检测try-error类的函数,帮助找到错误位置
is.error <- function(x) inherits(x, "try-error")
succeeded <- !vapply(results, is.error, logical(1))

# look at successful results
str(results[succeeded])
#> List of 3
#>  $ : num [1:10] 0 0.693 1.099 1.386 1.609 ...
#>  $ : num [1:2] NaN 2.3
#>  $ : num [1:2] 0 -Inf

# look at inputs that failed
str(elements[!succeeded])
#> List of 1
#>  $ : chr [1:26] "a" "b" "c" "d" ...
  • try()可以在表达式失败时,使用默认值,记得在try()外面赋值就行
default <- NULL
try(default <- read.csv("possibly-bad-input.csv"), silent = TRUE)
  • tryCatch()将条件映射到处理程序(handler),只能捕获特定类型的错误,而不是依赖于错误字符串的比较
show_condition <- function(code) {
  tryCatch(code,
    error = function(c) "error",
    warning = function(c) "warning",
    message = function(c) "message"
  )
}
show_condition(stop("!"))
#> [1] "error"
show_condition(warning("?!"))
#> [1] "warning"
show_condition(message("?"))
#> [1] "message"

# If no condition is captured, tryCatch returns the 
# value of the input
show_condition(10)
#> [1] 10
  • tryCatch()实现try()
try2 <- function(code, silent = FALSE) {
  tryCatch(code, error = function(c) {
    msg <- conditionMessage(c)
    if (!silent) message(c)
    invisible(structure(msg, class = "try-error"))
  })
}

try2(1)
#> [1] 1
try2(stop("Hi"))
#> Error in doTryCatch(return(expr), name, parentenv, handler): Hi
try2(stop("Hi"), silent = TRUE)
  • 修改存储在错误条件对象中的消息,让错误信息更详细
read.csv2 <- function(file, ...) {
  tryCatch(read.csv(file, ...), error = function(c) {
    c$message <- paste0(c$message, " (in ", file, ")")
    stop(c)
  })
}
read.csv("code/dummy.csv")
#> Error in file(file, "rt"): cannot open the connection
read.csv2("code/dummy.csv")
#> Error in file(file, "rt"): cannot open the connection (in code/dummy.csv)
  • WithCallingHandlers(), 哦对,多说一点,这种函数命名的方法称为驼峰法(单词首字母大写);tryCatch()的替代函数,但有些许区别。前者中的处理程序是在产生条件的调用的上下文中被调用的,而后者中的处理程序是在tryCatch()的上下文被调用的。大多数情况下不去使用WithCallingHandlers()
f <- function() g()
g <- function() h()
h <- function() stop("!")

tryCatch(f(), error = function(e) print(sys.calls()))
# [[1]] tryCatch(f(), error = function(e) print(sys.calls()))
# [[2]] tryCatchList(expr, classes, parentenv, handlers)
# [[3]] tryCatchOne(expr, names, parentenv, handlers[[1L]])
# [[4]] value[[3L]](cond)

withCallingHandlers(f(), error = function(e) print(sys.calls()))
# [[1]] withCallingHandlers(f(), 
#    error = function(e) print(sys.calls()))
# [[2]] f()
# [[3]] g()
# [[4]] h()
# [[5]] stop("!")
# [[6]] .handleSimpleError(
#    function (e) print(sys.calls()), "!", quote(h()))
# [[7]] h(simpleError(msg, call))
  • 防御性编程,这个我不懂,不写了

相关文章

  • 6. 调试、条件处理和防御式编程

    这章之前有章讲环境的,经常用到的概念和遇到的坑在前面或多或少都有涉及,所以跳过了。这章简写为debug 追溯错误发...

  • 一些开发规范

    编程规范 防御式编程,根据有限枚举先处理断言,再处理错误,最后处理正常逻辑,正常逻辑外尽可能多的处理异常分支;开发...

  • 安全编程的实现方式

    借鉴文章 契约式编程与防御式编程 https://segmentfault.com/a/1190000007558...

  • 防御式编程

    最近业余时间在阅读《代码大全》,阅读“防御式编程”章节的时候非常受启发,自己之前对系统的错误处理这块也确实随意了。...

  • 防御式编程

    编写优秀的代码 代码是程序可识别的代码 代码是程序员可识别的代码 防御性编程 防御性编程(Defensive pr...

  • 防御式编程

    防御式编程 在防御式驾驶中要建立这样一种思维,那就是你永远也不能确定另一位司机将要做什么。这样才能够确保在其他人做...

  • python必知必会7

    Python 支持函数式编程吗? 近些年来,由于函数式编程易于调试和测试的优点,函数式编程越来越受到关注。虽然 P...

  • 契约式编程与防御式编程

    1. 防御式编程 对于 “防御式编程” ,《代码大全》给我们提供了一个定义: 人类都是不安全、不值得信任的,所有的...

  • 防御式编程EAFP vs LBYL

    防御式编程EAFP vs LBYL EAFP:easier to ask forgiveness than per...

  • 防御式编程(二)

    《代码大全2》阅读随笔 错误处理技术 一些系统遇到错误后的指导建议 返回中立值; 换下一个正确数据; 返回前次相同...

网友评论

    本文标题:6. 调试、条件处理和防御式编程

    本文链接:https://www.haomeiwen.com/subject/jfqdvhtx.html