31

考虑我们已经调用debug()了几个函数来在它们上设置断点。当我们发现并解决错误时,是否undebug()所有功能都已debug()由单个命令标记?

这是一个很好的基准,可以查看您提出的方法是否真的完美运行:

> library(limma) # bioconductor
> debug(read.ilmn)
> read.ilmn("a.txt") # No problem if this file does not exist
Browse[2]> debug(.read.oneilmnfile) # This is the debug browser for read.ilmn()
Browse[2]> Q # To exit debug browser
> undebug.all() # Here run your proposed function to undebug everything!
> read.ilmn("a.txt")
# Now if the debug browser is not started, you are lucky to pass this test!

您可能会在下面看到接受的答案。任何此答案不起作用的情况或更清洁的版本都非常受欢迎。

4

3 回答 3

16

undebug()不,所有功能都没有完全可靠的方法。(我之所以这么说,是因为我已经在 R-devel 和 R-help 上看到过多次讨论。)

在本次讨论中,Brian Ripley 参与了讨论,并指出:

调试是函数对象的一个​​属性(在 sxpinfo 中有一点),因此您必须遍历所有可访问的对象(如 gc 所做的那样)才能找到它们。

这是一个片段,其中 Robert Gentleman 回答了一个问题(是否定的)“是否有一种方便的方法可以随时知道在R 会话中debug()trace()在 R 会话中标记的函数”:

您可能没有得到答案,因为答案是否定的,没有简单的方法。

于 2012-10-09T20:04:17.697 回答
16

这是我的解决方案...

编辑:修改以处理在命名空间中查找对象。代码已经变得有点笨拙了,因为我不太了解操作/查询命名空间的方法,而且我一直在反复试验。更清洁的版本将受到欢迎。几乎可以肯定还有其他极端情况会失败。

## return the names of the objects (from a vector of list of
## names of objects) that are functions and have debug flag set
isdebugged_safe <- function(x,ns=NULL)  {
    g <- if (is.null(ns)) get(x) else getFromNamespace(x,ns)
    is.function(g) && isdebugged(g)
}

which_debugged <- function(objnames,ns=NULL) {
    if (!length(objnames)) return(character(0))
    objnames[sapply(objnames,isdebugged_safe,ns=ns)]
}

all_debugged <- function(where=search(), show_empty=FALSE) {
    ss <- setNames(lapply(where,function(x) {
        which_debugged(ls(x,all.names=TRUE))
        }),gsub("package:","",where))
    ## find attached namespaces
    ## (is there a better way to test whether a 
    ##    namespace exists with a given name??)
    ns <- unlist(sapply(gsub("package:","",where),
                 function(x) {
                     if (inherits({n <- try(getNamespace(x),silent=TRUE)},
                         "try-error")) NULL else x
                 }))
    ss_ns <- setNames(lapply(ns,function(x) {
        objects <- ls(getNamespace(x),all.names=TRUE)
        which_debugged(objects,ns=x)
        }),ns)
    if (!show_empty) {
        ss <- ss[sapply(ss,length)>0]
        ss_ns <- ss_ns[sapply(ss_ns,length)>0]
    }
    ## drop overlaps
    for (i in names(ss))
        ss_ns[[i]] <- setdiff(ss_ns[[i]],ss[[i]])
    list(env=ss,ns=ss_ns)
}

undebug_all <- function(where=search()) {
    aa <- all_debugged(where)
    lapply(aa$env,undebug)
    ## now debug namespaces
    invisible(mapply(function(ns,fun) {
        undebug(getFromNamespace(fun,ns))
    },names(aa$ns),aa$ns))
}

该代码也发布在http://www.math.mcmaster.ca/bolker/R/misc/undebug_all.R

例子:

library(nlme)
debug(lme)
## define functions
source(url("http://www.math.mcmaster.ca/bolker/R/misc/undebug_all.R"))
undebug_all()
fm1 <- lme(distance ~ age, data = Orthodont) # from ?lme

在这种情况下lme,无需进入调试器即可运行。

另一个更难的例子:

library(limma)
source(url("http://www.math.mcmaster.ca/bolker/R/misc/undebug_all.R"))
debug(read.ilmn)
debug(limma:::.read.oneilmnfile)
all_debugged()
undebug_all()
read.ilmn()
read.ilmn("a.txt")

请注意,从调试的角度来看read.ilmn()read.ilmn("a.txt")它的行为似乎有所不同(我不明白为什么......)

于 2012-10-09T20:06:04.360 回答
8

这是一种选择,假设您正在调试的功能位于工作区或全局环境中。可以指定任何特定的环境,使其具有适应性,但这不会一次性适用于所有已加载包中的任何功能。

首先通过全局环境中的几个函数来说明:

> bar <- function() {}
> foo <- function() {}

用于lsf.str()返回工作区中的函数(供我们稍后使用unclass()并将其转换为列表):

> funlist <- as.list(unclass(lsf.str()))
> funlist
[[1]]
[1] "bar"

[[2]]
[1] "foo"

接下来,为这些函数生成一个关于它们是否被调试的指示器:

> debugged <- sapply(funlist, isdebugged)
> debugged
[1] FALSE FALSE

好的,所以debug()其中一个功能并重新运行:

> debug(bar)
> 
> debugged <- sapply(funlist, isdebugged)
> debugged
[1]  TRUE FALSE

最后,调试应用于它们sapply()funlist函数:undebug()

> sapply(funlist[debugged], undebug)
[[1]]
NULL

这当然可以封装成一个函数

undebugFuns <- function() {
    funs <- unclass(lsf.str())
    dbg <- sapply(funs, isdebugged)
    if(isTRUE(any(dbg))) {
        writeLines(paste("Un-debugging:", funs[dbg]))
        sapply(funs[dbg], undebug)
    } else {
        writeLines(paste("Nothing to debug"))
    }
    invisible()
}

> debug(bar)
> undebugFuns()
Un-debugging: bar

一种未被采用的调试isdebugged()是通过以下方式制定的debugonce()

> debug(bar)
> isdebugged(bar)
[1] TRUE
> undebugFuns()
Un-debugging: bar
> debugonce(bar)
> isdebugged(bar)
[1] FALSE

这再次说明了乔什在他的回答中的观点。

于 2012-10-09T20:02:37.110 回答