记录当前的功能名称 [英] Logging current function name

查看:91
本文介绍了记录当前的功能名称的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

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

现在,我用(自制)函数工作很多,并使用其中的一些logfuntions来查看进度,工作得很好。但我注意到,我几乎总是使用这些函数:

  somefunc< -function(blabla)
{
catt(somefunc:start)
#在这里有一些非常有用的东西
catt(somefunc:稍后一段时间)
#even更有用的东西
catt(somefunc:end)
}

catt 以它从中调用的函数的名称开始。非常整齐,直到我开始重构我的代码和重命名函数等。

感谢Brian Ripley的一些旧的R-list帖子,如果我没有弄错,我发现获取'当前函数名'的代码:

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

file = file,sep = sep,fill = fill,labels = labels,append = append)$




$ b

这非常好,但它并不总是有效,因为:
$ b $> p>


  • 我的函数分散在 lapply中使用的匿名函数
    函数类型,像这样:


    blockquote
    $ $ $ $ $ $ $ $ $ aFunc< -function(somedataframe)
    {
    结果< -lapply(s eq_along(somedataframe),function(i){
    catw(working col,i,/,ncol(somedataframe))
    #在这里做更多的东西并返回一些东西
    return(sum(is.na(somedataframe [[i]]))
    }
    }


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




  • 偶尔使用 do。调用:看来我目前的实现
    也不行(我再次可以理解它,尽管
    我还没有完全弄清楚。



  • $ p所以,我的问题是:有没有办法在callstack中找到第一个名为的函数(跳过记录函数本身以及其他一些众所周知的异常),这将允许我为所有情况编写单一版本的 catw (这样我就可以愉快地重构而不用担心我的日志代码)?你会如何去做这样的事情?



    编辑:这些情况应该得到支持:

      testa <-function(par1)
    {
    catw(您好,testa,par1 =,par1)
    for(i in 1:2)catw(testa,item的正常循环,i)
    rv <-sapply(1:2,function(i){catw(testa,item,i) i)})
    return(rv)
    }

    testb< -function(par1,par2)
    {
    catw(Hello from testb, par1 =,par1)
    for(i in 1:2)catw(testb,item的正常循环,i)
    rv <-sapply(1:2,function(i){catw (testb,item,i); return(i)})

    catw(现在将从testb调用testa)
    rv2< -testa(par1)
    catw(testa回调testb)

    catw(现在可以从testb执行test.c)
    rv2< -do.call(testa,list(par1 ))
    catw(从testa的testa do.call返回)

    return(list(rv,rv2))
    }

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


    解决方法

    编辑:完全重写函数



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



    调用堆栈包含完整的调用函数。所以现在的诀窍是只提取你真正想要的位。我在 clean_cs 函数中使用了一些手动清理。这将评估调用堆栈中的第一个单词,并为少量已知边缘情况返回所需的参数,特别是 lapply sapply do.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 fr om testa,par1 = 123 
    testa从testa正常循环,项目1
    testa正常循环从testa,项目2
    testa sapply从testa,项目1
    testa sapply from testa,项目2


    testb Hello your testb,par1 = 123
    testb从testb正常循环,项目1
    testb从testb正常循环,项目2
    从testb的testb sapply项目1
    从testb的项目2的testb sapply项目2
    testb现在将调用来自testb的testa
    testb | testa Hello your testa,par1 = 123
    testb | testa正常循环从testa,项目1
    testb | testa正常循环从testa,项目2
    testb | testa sapply从testa,项目1
    testb | testa sapply从testa,项目2
    testb从testa中返回testb
    testb现在从testb执行test.c $ testb testa testa testa testa test1 hello testa testa testa testa testa testa b $ b testb | testa来自testa的正常循环,项目2
    testb | testa来自testa的sapply,项目1
    testb | testa从testa项目2中的sapply
    testb从testa do.c返回所有in testb


    testb Hello your testb,par1 = 123
    testb从testb正常循环,第1项
    testb从testb正常循环,第2项
    testb sapply from testb,item 1
    testb sapply from testb,item 2
    testb现在将从testb调用testa
    testb | testa Hello your testa,par1 = 123
    testb | testa正常循环从testa,项目1
    testb | testa正常循环从testa,项目2
    testb | testa sapply从testa,项目1
    testb | testa sapply从testa,项目2
    testb从testa中返回testb
    testb现在将从testb中执行test.c $ testb | testa testa中的testa,par1 = 123 $ b $ testa | testa testa中的正常循环,项目1
    testb | testa从testa正常循环,项目2
    testb | testa从testa中输出sapply,项目1
    testb | testa sapply from testa,项目2
    testb从testa返回。调用testb


    I have a few custom logfunctions that are extensions of cat. A basic example is something like this:

    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)
    }
    

    Now, I work a lot with (selfmade) functions, and use some of these logfuntions to see the progress, which works quite well. What I notice, though, is that I almost always use these functions like this:

    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")
    }
    

    Notice how every call to catt begins with the name of the function it is called from. Very neat until I start to refactor my code and rename functions etc.

    Thanks to some old R-list post from Brian Ripley, if I'm not mistaken, I found this code to get the 'current function name':

    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)
    }
    

    This is very nice, but it doesn't always work, because:

    • my functions are scattered with anonymous functions used in lapply type of functions, like this:

    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]])))
      }
    }
    

    -> for these cases, apparently (and understandably) I need n=3 in the sys.parent call in my catw function.

    • I occasionally use do.call: it appears my current implementation doesn't work either (once again I can somewhat understand it, though I haven't figured it out completely.

    So, my question is: is there a way to find the first named function higher in the callstack (skipping the logging function itself, and maybe some other "wellknown" exceptions), which would allow me to write one single version of catw for all cases (so that I can happily refactor without worrying about my logging code)? How would you go about something like this?

    Edit: these cases should be supported:

    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))
    

    解决方案

    EDIT : Complete rewrite of function

    The new version of this function uses the call stack, sys.calls(), rather than match.call.

    The call stack contains the complete calling function. So the trick now is to only extract the bits of it that you really want. I have resorted to a bit of manual cleanup in the clean_cs function. This evaluates the first word in the call stack and returns the desired argument for a small number of known edge cases, in particular lapply, sapply and do.call.

    The only downside of this approach is that it will return function names all the way to the top of the call stack. Perhaps a logical next step would be to compare these functions with a spefified environment/namespace and include/exclude function names based on that...

    I shall stop here. It answers to the use cases in the question.


    The new function:

    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="|")
    }
    


    Test results:

    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
    

    这篇关于记录当前的功能名称的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆