代码之家  ›  专栏  ›  技术社区  ›  Tal Galili

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

  •  6
  • Tal Galili  · 技术社区  · 15 年前

    在调试我通常使用的函数时

    库(调试)
    mtrace(函数名)
    函数名(…)
    < /代码> 
    
    

    这对我来说非常有效。

    然而,有时我试图调试一个我不知道的复杂函数。在这种情况下,我可以发现在该函数内部有另一个我想“进入”(“调试”)的函数,因此为了更好地理解整个过程是如何工作的。

    所以一种方法是:

    库(调试)
    mtrace(函数名)
    函数名(…)
    #在函数内部查找要调试的函数时,请再次运行:
    mtrace(函数名.子函数)
    < /代码> 
    
    

    问题是-是否有更好/更聪明的方法来进行交互式调试(如我所描述的),而我可能会错过?

    P.S:我知道在SO上,有很多关于主题的问题(请参见这里)。然而,我在这里所问的问题/解决方案却不尽相同。

    这对我来说很有效。

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

    所以一种方法是:

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

    问题是-有没有更好/更聪明的方法来进行交互式调试(如我所描述的),我可能会错过?

    P.S:我知道,在这里,关于SO主题的各种问题(参见在这里)然而,我在这里所问的问题/解决方案却不尽相同。

    3 回复  |  直到 15 年前
        1
  •  5
  •   Community CDub    8 年前

    不完全确定用例,但是当遇到问题时,可以调用函数 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)
    

    依我看,这似乎不是最明智的做法。更合理的做法是简单地进入问题发生的函数(即在最低级别),然后向后工作。

    我已经在 "favorite debugging trick" .

        2
  •  5
  •   Community CDub    8 年前

    我喜欢 options(error=recover) 作为详细 previously on SO . 然后事情就停在错误的地方,人们可以检查。

        3
  •  3
  •   Mark Bravington    15 年前

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

    如果“子函数”的定义不在“MyFunction”范围内,则可以只对“子函数”进行mtrace操作,而不需要对“MyFunction”进行mtrace操作。如果功能没有“mtrace”的话运行得更快,所以最好只在需要的时候进行mtrace。(但你可能已经知道这些了!)

    如果“myfunction”只在“subfunction”中定义,那么可能有助于使用“myfunction”中的条件断点。您需要“mtrace(myfunction)”,然后运行它,当出现调试窗口时,找出在中定义了“myfunction”行。假设是17号线。那么,以下内容应该有效:

    d(n)>bp(1,f)不再显示MyFunction的窗口 d(n)>bp(18,mtrace(子功能);false) D(n)& gt;()

    这应该很清楚它的作用(或者如果你尝试它的话)。

    唯一的缺点是:每当您更改“myfunction”的代码时,都需要再次执行此操作;并且,通过“myfunction”本身可能发生的速度减慢正在被跟踪。

    您还可以尝试将“debug.sub”参数添加到“myfunction”,默认值为false。在“myfunction”代码中,在“subfunction”定义后立即添加此行:

    if(debug.sub)mtrace(子函数)

    这避免了对mtrace“myfunction”本身的任何需要,但确实要求您能够更改其代码。