记录当前的功能名称 [英] Logging current function name
问题描述
我有一些自定义的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。调用
:看来我目前的实现
也不行(我再次可以理解它,尽管
我还没有完全弄清楚。 - my functions are scattered with anonymous functions used in
lapply
type of functions, like this: - 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.
$ 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:
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.
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屋!