为R函数编写装饰器

22

我的同事最近在查看调用图,并希望看到哪个函数被调用了。我们使用了mvbutils中的foodweb来解决这个问题,但我想知道如何在R中创建一个最佳的装饰器 (python中的说法)。所以我做了这个:

instrument=function(z){
  force(z) 
  n=deparse(substitute(z)) # get the name
  f=function(...){
   cat("calling ", n,"\n")
   x=z(...)
   cat("done\n")
   return(x)
   }
  return(f)
}

这让我能够做到:
> foo=function(x,y){x+y}
> foo(1,2)
[1] 3

现在,我可以通过包装函数来使其自行记录:

> foo=instrument(foo)
> foo(1,2)
calling  foo
done
[1] 3

这个之前做过吗?比如说在某个软件包中,我有没有错过会破坏我这种方式的陷阱?


2
公正的问题,但问错了人群。在我看来,你显然需要在r-devel上提出这个问题。 - Dirk Eddelbuettel
3
只要 Gabor 在观众席上,那就是正确的观众。 - Spacedman
3
好问题。虽然已经有答案被接受了,但我想看到一个更加常规/百科全书式的答案 :) - fmark
2个回答

13

R中的trace函数可以实现这一功能。请参见?trace


4
我的 Github 包中的标签尝试解决这个问题。
你的例子可以如下解决:
# remotes::install_github("moodymudskipper/tag")
library(tag)
deco <- tag(args = list(.first = NULL, .last = NULL), pattern = {
  t_args <- T_ARGS()                       # fetch arguments fed to tag
  eval.parent(t_args[[".first"]])          # run .first arg
  on.exit(eval.parent(t_args[[".last"]]))  # run .last arg on exit
  CALL()                                   # run main call
})


foo <- function(x, y) {Sys.sleep(1); x + y} # sleep 1 sec to highlight expected behavior

deco(quote(message("calling foo")), quote(message("done")))$foo(1, 2)
#> calling foo
#> done
#> [1] 3

foo2 <- deco(quote(message("calling foo")), quote(message("done")))$foo
foo2(1, 2)
#> calling foo
#> done
#> [1] 3

deco2 <- deco(quote(message("calling foo")), quote(message("done")))
deco2$foo(1, 2)
#> calling foo
#> done
#> [1] 3

创建于2020年1月30日,使用reprex package (v0.3.0)

标签是函数操作器工厂(或副词工厂),这里的deco是一个标签,deco(quote(message("calling foo")), quote(message("done")))是一个副词,具有$方法。这意味着您可以运行deco(quote(message("calling foo")), quote(message("done")))(foo)(1,2),但美元符号表示法使其更加友好。

标签定义包括默认参数(默认值是强制性的,不支持省略号)和一个pattern,它有点像您的新主体,使用特殊函数T_ARGS()F_ARGS()F_ARGS()F_FORMALS()CALL()来访问标签或函数的参数或形式以及调用本身(请参见?tag::CALL)。

实现了一些更多的魔法,使得标记的参数可以传递给带有标记的函数本身,因此也可以执行以下操作:

deco$foo(1, 2, quote(message("calling foo")), quote(message("done")))
#> calling foo
#> done
#> [1] 3
foo2 <- deco$foo
foo2(1, 2, quote(message("calling foo")), quote(message("done")))
#> calling foo
#> done
#> [1] 3

在这些情况下,您可以在RStudio中享受自动完成功能:

enter image description here

更多信息:https://github.com/moodymudskipper/tag 标签包含一系列这样的“装饰器”。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接