我有一些自定义log功能,它们是cat的扩展。一个基本的例子是这样的:

catt<-function(..., file = "", sep = " ", fill = FALSE, labels = NULL,
    append = FALSE)
{
    cat(..., format(Sys.time(), "(%Y-%m-%d %H:%M:%S)"), "\n", file = file,
        sep = sep, fill = fill, labels = labels, append = append)
}


现在,我使用(自制的)函数进行了大量工作,并使用其中的一些logfuntions来查看进度,效果很好。不过,我注意到的是,我几乎总是像这样使用这些功能:

somefunc<-function(blabla)
{
  catt("somefunc: start")
  #do some very useful stuff here
  catt("somefunc: some time later")
  #even more useful stuff
  catt("somefunc: the end")
}


注意,每次对catt的调用都是如何从其调用函数的名称开始的。直到我开始重构代码并重命名函数等之前,它都非常整洁。

多亏了Brian Ripley的一些旧R-list帖子,如果我没记错的话,我找到了这段代码来获取“当前函数名称”:

catw<-function(..., file = "", sep = " ", fill = FALSE, labels = NULL,
    append = FALSE)
{
    curcall<-sys.call(sys.parent(n=1))
    prefix<-paste(match.call(call=curcall)[[1]], ":", sep="")
    cat(prefix, ..., format(Sys.time(), "(%Y-%m-%d %H:%M:%S)"), "\n",
        file = file, sep = sep, fill = fill, labels = labels, append = append)
}


这非常好,但是并不总是有效,因为:


我的函数分散在lapply中使用的匿名函数中
函数类型,如下所示:



aFunc<-function(somedataframe)
{
  result<-lapply(seq_along(somedataframe), function(i){
  catw("working on col", i, "/", ncol(somedataframe))
  #do some more stuff here and return something
  return(sum(is.na(somedataframe[[i]])))
  }
}



->对于这些情况,显然(并且可以理解)在我的sys.parent函数的catw调用中我需要n = 3。


我偶尔使用do.call:似乎是我当前的实现
也不起作用(虽然再次,我可以有所了解
我还没有完全弄清楚。


因此,我的问题是:有没有办法在调用堆栈中找到更高的第一个命名函数(跳过日志记录函数本身,以及其他一些“众所周知的”异常),这将允许我编写一个单一版本的catw对于所有情况(这样我就可以愉快地进行重构而不必担心我的日志代码)?你会怎么做这样的事情?

编辑:应支持以下情况:

testa<-function(par1)
{
    catw("Hello from testa, par1=", par1)
    for(i in 1:2) catw("normal loop from testa, item", i)
    rv<-sapply(1:2, function(i){catw("sapply from testa, item", i);return(i)})
    return(rv)
}

testb<-function(par1, par2)
{
    catw("Hello from testb, par1=", par1)
    for(i in 1:2) catw("normal loop from testb, item", i)
    rv<-sapply(1:2, function(i){catw("sapply from testb, item", i);return(i)})

    catw("Will now call testa from testb")
    rv2<-testa(par1)
    catw("Back from testa call in testb")

    catw("Will now do.call testa from testb")
    rv2<-do.call(testa, list(par1))
    catw("Back from testa do.call in testb")

    return(list(rv, rv2))
}

testa(123)
testb(123,456)
do.call(testb, list(123,456))

最佳答案

编辑:完全重写功能

此函数的新版本使用调用堆栈sys.calls(),而不是match.call

调用堆栈包含完整的调用函数。因此,现在的诀窍是仅提取您真正想要的部分。我在clean_cs函数中采取了一些手动清理的方法。这将评估调用堆栈中的第一个单词,并为少数已知的边缘情况(尤其是lapplysapplydo.call)返回所需的自变量。

这种方法的唯一缺点是,它将把函数名称一直返回到调用堆栈的顶部。合理的下一步可能是将这些功能与特定的环境/名称空间进行比较,并基于该功能包含/排除功能名称...

我会在这里停止。它回答了问题中的用例。



新功能:

catw <- function(..., callstack=sys.calls()){
  cs <- callstack
  cs <- clean_cs(cs)
  #browser()
  message(paste(cs, ...))
}

clean_cs <- function(x){
  val <- sapply(x, function(xt){
    z <- strsplit(paste(xt, collapse="\t"), "\t")[[1]]
    switch(z[1],
        "lapply" = z[3],
        "sapply" = z[3],
        "do.call" = z[2],
        "function" = "FUN",
        "source" = "###",
        "eval.with.vis" = "###",
        z[1]
        )
    })
  val[grepl("\\<function\\>", val)] <- "FUN"
  val <- val[!grepl("(###|FUN)", val)]
  val <- head(val, -1)
  paste(val, collapse="|")
}




检测结果:

testa Hello from testa, par1= 123
testa normal loop from testa, item 1
testa normal loop from testa, item 2
testa sapply from testa, item 1
testa sapply from testa, item 2


testb Hello from testb, par1= 123
testb normal loop from testb, item 1
testb normal loop from testb, item 2
testb sapply from testb, item 1
testb sapply from testb, item 2
testb Will now call testa from testb
testb|testa Hello from testa, par1= 123
testb|testa normal loop from testa, item 1
testb|testa normal loop from testa, item 2
testb|testa sapply from testa, item 1
testb|testa sapply from testa, item 2
testb Back from testa call in testb
testb Will now do.call testa from testb
testb|testa Hello from testa, par1= 123
testb|testa normal loop from testa, item 1
testb|testa normal loop from testa, item 2
testb|testa sapply from testa, item 1
testb|testa sapply from testa, item 2
testb Back from testa do.call in testb


testb Hello from testb, par1= 123
testb normal loop from testb, item 1
testb normal loop from testb, item 2
testb sapply from testb, item 1
testb sapply from testb, item 2
testb Will now call testa from testb
testb|testa Hello from testa, par1= 123
testb|testa normal loop from testa, item 1
testb|testa normal loop from testa, item 2
testb|testa sapply from testa, item 1
testb|testa sapply from testa, item 2
testb Back from testa call in testb
testb Will now do.call testa from testb
testb|testa Hello from testa, par1= 123
testb|testa normal loop from testa, item 1
testb|testa normal loop from testa, item 2
testb|testa sapply from testa, item 1
testb|testa sapply from testa, item 2
testb Back from testa do.call in testb

关于r - 记录当前功能名称,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/7307987/

10-12 22:38
查看更多