如何以编程方式创建R函数?

如何以编程方式创建R函数?,r,function,R,Function,最近在列表上问了一个有趣的问题,由于找不到关于StackOverflow主题的现有问题,我认为它可能会有用,因为它也存在于这里 意译: R函数由三个元素组成:参数列表、主体和环境。我们能用这三个元素以编程方式构造函数吗? (在上面的r-devel链接中,在线程的末尾给出了一个相当全面的答案。我会让其他人自己重新创建各种解决方案的基准测试,并将其作为一个答案提供,但如果您这样做,请务必引用Hadley。如果几个小时内没有人站出来,我会自己做。)这是对讨论的扩展 我们的三个部分需要是一个参数列表、一

最近在列表上问了一个有趣的问题,由于找不到关于StackOverflow主题的现有问题,我认为它可能会有用,因为它也存在于这里

意译:

R函数由三个元素组成:参数列表、主体和环境。我们能用这三个元素以编程方式构造函数吗?


(在上面的r-devel链接中,在线程的末尾给出了一个相当全面的答案。我会让其他人自己重新创建各种解决方案的基准测试,并将其作为一个答案提供,但如果您这样做,请务必引用Hadley。如果几个小时内没有人站出来,我会自己做。)这是对讨论的扩展

我们的三个部分需要是一个参数列表、一个主体和一个环境

对于环境,默认情况下我们只需使用
env=parent.frame()

我们并不真正希望参数有一个常规的旧列表,因此我们使用
alist
它有一些不同的行为:

“…不计算值,不允许使用无值的标记参数”

一个选项是将
args
转换为成对列表,然后只需调用函数
function
使用
eval

make_function1 <- function(args, body, env = parent.frame()) {
      args <- as.pairlist(args)
      eval(call("function", args, body), env)
}
最后,对我来说,这似乎与第一种方法非常相似,除了 我们使用一种稍微不同的习惯用法来创建函数调用,使用
替换
而不是调用:

body <- quote(a + b)
make_function4 <- function(args, body, env = parent.frame()) {
      subs <- list(args = as.pairlist(args), body = body)
      eval(substitute(`function`(args, body), subs), env)
}


library(microbenchmark)
microbenchmark(
      make_function1(args, body),
      make_function2(args, body),
      make_function3(args, body),
      make_function4(args, body),
      function(a = 1, b = 2) a + b
    )

Unit: nanoseconds
                          expr   min      lq  median      uq    max
1 function(a = 1, b = 2) a + b   187   273.5   309.0   363.0    673
2   make_function1(args, body)  4123  4729.5  5236.0  5864.0  13449
3   make_function2(args, body) 50695 52296.0 53423.0 54782.5 147062
4   make_function3(args, body)  8427  8992.0  9618.5  9957.0  14857
5   make_function4(args, body)  5339  6089.5  6867.5  7301.5  55137

make_function4还有以编程方式创建
alist
对象的问题,因为当参数的数量可变时,这对创建函数很有用

alist
只是一个空符号的命名列表。可以使用
substitute()
创建这些空符号。因此:

make_alist <- function(args) {
  res <- replicate(length(args), substitute())
  names(res) <- args
  res
}

identical(make_alist(letters[1:2]), alist(a=, b=))
## [1] TRUE

make\u alist
rlang
有一个名为
new\u function
的函数,可以执行以下操作:

用法

新的函数(args,body,env=caller\u env())

库(rlang)

g我不确定这是否有帮助,但下面的代码在某些情况下可能是有益的

hello\u world可以是用于创建函数的字符串,而assign将用于命名函数hello\u world

hello_world <- "print('Hello World')"

assign("Hello",function()
  {
    eval(parse(text = hello_world))
  }, envir = .GlobalEnv)

hello\u world我想这就是他们发明lisps的原因!听起来可能很奇怪,但我们已经看到一个案例,其中一个人可能想要实现类似的目标(knitr)。joran,非常感谢。请问您对基准测试有何评论?从结果看,函数1比其他函数稍微快一点。但是有一个愚蠢的选择:`函数(a=1,b=2,c=3,d=4,e=5,f=6,g=7,h=8,i=9,j=10){exp(a+b)*(c+d)^e/f-ln(g)+h^i^j}`我知道函数4比其他函数看起来快得多。有什么想法吗?在代码中扩展
make_function1
以避免覆盖现有函数,我提出了以下建议:
make_function4 <- function(args, body, env = parent.frame()) {
      subs <- list(args = as.pairlist(args), body = body)
      eval(substitute(`function`(args, body), subs), env)
}


library(microbenchmark)
microbenchmark(
      make_function1(args, body),
      make_function2(args, body),
      make_function3(args, body),
      make_function4(args, body),
      function(a = 1, b = 2) a + b
    )

Unit: nanoseconds
                          expr   min      lq  median      uq    max
1 function(a = 1, b = 2) a + b   187   273.5   309.0   363.0    673
2   make_function1(args, body)  4123  4729.5  5236.0  5864.0  13449
3   make_function2(args, body) 50695 52296.0 53423.0 54782.5 147062
4   make_function3(args, body)  8427  8992.0  9618.5  9957.0  14857
5   make_function4(args, body)  5339  6089.5  6867.5  7301.5  55137
make_alist <- function(args) {
  res <- replicate(length(args), substitute())
  names(res) <- args
  res
}

identical(make_alist(letters[1:2]), alist(a=, b=))
## [1] TRUE
library(rlang)
g <- new_function(alist(x = ), quote(x + 3))
g
# function (x) 
# x + 3
hello_world <- "print('Hello World')"

assign("Hello",function()
  {
    eval(parse(text = hello_world))
  }, envir = .GlobalEnv)