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