了解和使用基本的 R 调试工具是学习快速调试 R 代码必不可少的第一步。如果您知道如何使用基本工具,则可以在任何地方调试代码,而无需附加包中提供的所有额外工具。
traceback() 可以让你看到导致错误的调用堆栈
foo <- function(x) {
d <- bar(x)
x[1]
}
bar <- function(x) {
stopifnot(is.matrix(x))
dim(x)
}
foo(1:10)
traceback()
产量:
> foo(1:10)
Error: is.matrix(x) is not TRUE
> traceback()
4: stop(paste(ch, " is not ", if (length(r) > 1L) "all ", "TRUE",
sep = ""), call. = FALSE)
3: stopifnot(is.matrix(x))
2: bar(x)
1: foo(1:10)
所以我们可以清楚地看到错误发生在函数bar();我们缩小了寻找错误的范围。但是,如果代码生成警告而不是错误怎么办?这可以通过 warn 选项将警告变成错误来处理:
options(warn = 2)
会将警告变成错误。然后你可以使用traceback() 来追踪他们。
与此相关的是让 R 从代码中的错误中恢复,这样您就可以调试出了什么问题。每当出现错误时,options(error = recover) 会将我们放入调试器框架:
> options(error = recover)
> foo(1:10)
Error: is.matrix(x) is not TRUE
Enter a frame number, or 0 to exit
1: foo(1:10)
2: bar(x)
3: stopifnot(is.matrix(x))
Selection: 2
Called from: bar(x)
Browse[1]> x
[1] 1 2 3 4 5 6 7 8 9 10
Browse[1]> is.matrix(x)
[1] FALSE
您看到我们可以放入调用堆栈上的每一帧,看看函数是如何被调用的,参数是什么等等。在上面的例子中,我们看到 bar() 被传递了一个向量而不是一个矩阵,因此错误。 options(error = NULL) 将此行为重置为正常。
另一个关键函数是trace(),它允许您将调试调用插入到现有函数中。这样做的好处是您可以告诉 R 从源代码中的特定行进行调试:
> x <- 1:10; y <- rnorm(10)
> trace(lm, tracer = browser, at = 10) ## debug from line 10 of the source
Tracing function "lm" in package "stats"
[1] "lm"
> lm(y ~ x)
Tracing lm(y ~ x) step 10
Called from: eval(expr, envir, enclos)
Browse[1]> n ## must press n <return> to get the next line step
debug: mf <- eval(mf, parent.frame())
Browse[2]>
debug: if (method == "model.frame") return(mf) else if (method != "qr") warning(gettextf("method = '%s' is not supported. Using 'qr'",
method), domain = NA)
Browse[2]>
debug: if (method != "qr") warning(gettextf("method = '%s' is not supported. Using 'qr'",
method), domain = NA)
Browse[2]>
debug: NULL
Browse[2]> Q
> untrace(lm)
Untracing function "lm" in package "stats"
这允许您在代码中的正确位置插入调试调用,而无需逐步执行正在进行的函数调用。
如果您想在函数执行时单步执行,那么debug(foo) 将为函数foo() 打开调试器,而undebug(foo) 将关闭调试器。
关于这些选项的一个关键点是,我不需要修改/编辑任何源代码来插入调试调用等。我可以尝试一下,直接从发生错误的会话中查看问题所在。
有关 R 中调试的不同看法,请参阅 Mark Bravington 在 CRAN 上的 debug 包