Hadley Wickham最近在r-devel邮件列表上提出了一个有趣的问题,并且无法在StackOverflow上找到关于该主题的现有问题,我认为它也可能对它有用.
换句话说:
R函数由三个元素组成:参数列表,正文和环境.我们可以从这三个元素以编程方式构造函数吗?
(在上面的r-devel链接的主题结尾处得到了一个相当全面的答案.我将让其他人重新开放各种解决方案本身的基准并提供它作为答案,但一定要引用哈德利如果你这样做的话.如果没有人在几个小时内加强,我会自己做.)
jor*_*ran 48
这是在讨论扩展在这里.
我们的三件作品需要是一个参数列表,一个正文和一个环境.
对于环境,我们将env = parent.frame()
默认使用.
我们并不真的想要一个常规的旧参数列表,所以我们使用alist
哪个具有一些不同的行为:
"...不评估值,允许使用没有值的标记参数"
args <- alist(a = 1, b = 2)
Run Code Online (Sandbox Code Playgroud)
对于身体,我们quote
的表达得到一个call
:
body <- quote(a + b)
Run Code Online (Sandbox Code Playgroud)
一种选择是转换args
为一个pairlist,然后function
使用eval
以下命令调用该函数:
make_function1 <- function(args, body, env = parent.frame()) {
args <- as.pairlist(args)
eval(call("function", args, body), env)
}
Run Code Online (Sandbox Code Playgroud)
另一种选择是创建一个空函数,然后用所需的值填充它:
make_function2 <- function(args, body, env = parent.frame()) {
f <- function() {}
formals(f) <- args
body(f) <- body
environment(f) <- env
f
}
Run Code Online (Sandbox Code Playgroud)
第三种选择是简单地使用as.function
:
make_function3 <- function(args, body, env = parent.frame()) {
as.function(c(args, body), env)
}
Run Code Online (Sandbox Code Playgroud)
最后,这似乎与我的第一个方法非常相似,除了我们使用一个不同的习惯用来创建函数调用,
substitute
而不是call
:
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
Run Code Online (Sandbox Code Playgroud)
rlang
有一个调用的函数new_function
可以做到这一点:
用法
new_function(args, body, env = caller_env())
library(rlang)
g <- new_function(alist(x = ), quote(x + 3))
g
# function (x)
# x + 3
Run Code Online (Sandbox Code Playgroud)
还存在以alist
编程方式创建对象的问题,因为当参数数量可变时,这对于创建函数很有用。
An 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
Run Code Online (Sandbox Code Playgroud)