R中功能的“动态/交互式”调试建议?

时间:2010-07-09 12:28:51

标签: debugging r

调试函数时,我经常使用

library(debug)
mtrace(FunctionName)
FunctionName(...)

这对我来说非常有效。

但是,有时我试图调试一个我不知道的复杂函数。在这种情况下,我可以发现在该函数内部还有另一个函数,我想“进入”(“调试”) - 以便更好地理解整个过程的工作原理。

所以这样做的一种方法是:

library(debug)
mtrace(FunctionName)
FunctionName(...)
# when finding a function I want to debug inside the function, run again:
mtrace(FunctionName.SubFunction)

问题是 - 是否有更好/更智能的方式进行交互式调试(如我所述)我可能会丢失?

p.s:我知道在SO上有关于这个主题的各种问题(见here)。然而,我无法遇到类似的问题/解决方案,而不是我在这里提出的问题。

3 个答案:

答案 0 :(得分:5)

不完全确定用例,但遇到问题时,可以调用函数traceback()。这将显示函数调用栈的路径,直到遇到问题为止。如果您倾向于从顶部开始工作,可以在进行函数调用之前,在列表中给出的每个函数上调用debug。然后,您将从头开始学习整个过程。

以下是一个如何以更系统的方式执行此操作的示例,通过创建一个单步执行的功能:

walk.through <- function() {
  tb <- unlist(.Traceback)
  if(is.null(tb)) stop("no traceback to use for debugging")
  assign("debug.fun.list", matrix(unlist(strsplit(tb, "\\(")), nrow=2)[1,], envir=.GlobalEnv)
  lapply(debug.fun.list, function(x) debug(get(x)))
  print(paste("Now debugging functions:", paste(debug.fun.list, collapse=",")))
}

unwalk.through <- function() {
  lapply(debug.fun.list, function(x) undebug(get(as.character(x))))
  print(paste("Now undebugging functions:", paste(debug.fun.list, collapse=",")))
  rm(list="debug.fun.list", envir=.GlobalEnv)
}

以下是使用它的虚拟示例:

foo <- function(x) { print(1); bar(2) }
bar <- function(x) { x + a.variable.which.does.not.exist }
foo(2)

# now step through the functions
walk.through() 
foo(2)

# undebug those functions again...
unwalk.through()
foo(2)
IMO,这似乎不是最明智的事情。简单地进入问题发生的功能(即最低级别)并向后工作更有意义。

我已经在"favorite debugging trick"中概述了这个基本例程背后的逻辑。

答案 1 :(得分:5)

我喜欢options(error=recover)详细previously on SO。事情然后停在错误点,人们可以检查。

答案 2 :(得分:3)

(我是'mtrace'所在的'debug'包的作者)

如果'SubFunction'的定义位于'MyFunction'之外,那么你可以mtrace'SubFunction'并且不需要mtrace'MyFunction'。如果它们没有'mtrace',那么函数运行得更快,因此只需要尽可能少的mtrace就可以了。 (但你可能已经知道了这些事情!)

如果'MyFunction'仅在'SubFunction'中定义,可能有用的一个技巧是在'MyFunction'中使用条件断点。你需要'mtrace(MyFunction)',然后运行它,当调试窗口出现时,找出定义了'MyFunction'的行。说出它的第17行。然后以下内容应该有效:

d(n)的&GT; bp(1,F)#不再打扰再显示MyFunction的窗口 d(n)的&GT; bp(18,{mtrace(SubFunction); FALSE}) d(n)的&GT;去()

应该清楚这是做什么的(或者如果你尝试的话)。

唯一的缺点是:每当你改变'MyFunction'的代码时都需要再做一次,并且;通过'MyFunction'本身可能会发生的减速。

您还可以尝试向'MyFunction'添加'debug.sub'参数,默认为FALSE。在'MyFunction'的代码中,然后在'SubFunction'定义后立即添加这一行:

if(debug.sub)mtrace(SubFunction)

这样就不需要mtrace'MyFunction'本身,但确实需要你能够改变它的代码。