为R函数编写装饰器

时间:2011-01-28 14:53:06

标签: r

最近一位同事正在查看电话号码,并希望了解所谓的内容。我们用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 个答案:

答案 0 :(得分:10)

R中的trace函数可以做到这一点。请参阅?trace

答案 1 :(得分:4)

我的github软件包 tag 试图解决此问题。

您的示例可以如下解决:

# 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

reprex package(v0.3.0)于2020-01-30创建

标签是函数运算符工厂(或副词工厂),此处deco是标签,而deco(quote(message("calling foo")), quote(message("done")))是副词,带有$的方法。这意味着您可以运行deco(quote(message("calling foo")), quote(message("done")))(foo)(1,2),但是美元符号使它更友好。

标记定义具有默认参数(默认值是强制性的,不支持点),并且使用特殊功能pattern,{{1 }},T_ARGS()F_ARGS()F_ARGS()来访问标记或函数的参数或形式以及调用本身(请参见F_FORMALS())。

实现了更多的魔术,因此可以将标记的参数赋予标记函数本身,因此也可以执行以下操作:

CALL()

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

enter image description here

更多信息:https://github.com/moodymudskipper/tag 软件包tags包含此类“装饰器”的集合