我不确定我是否以正确的方式使用do.call
:
test <- function(test) {
string <- deparse(substitute(test))
start <- regexpr("\\(", string)
end <- regexpr(")", string) - 1
distribution <- substr(string, 0, start-1)
string.arguments <- substr(string, start+1, end)
v <- read.table(text=unlist(strsplit(string.arguments, ",")))
list.arguments <- lapply(t(v), function(x) x)
for (i in 1:1000000) {
do.call(distribution, list.arguments)
}
}
此处的目标是能够发送分发,例如rnorm
和rgamma
,然后发送函数的参数,而不是评估函数。
这是使用do.call和只是简单地调用函数的比较:
> system.time(test(rnorm(100, 1, 10)))
user system elapsed
17.772 0.000 17.820
> system.time(for(i in 1:1000000) { rnorm(100,0,1)} )
user system elapsed
13.940 0.004 14.015
问题有两个:
答案 0 :(得分:10)
do.call
总是比直接调用函数慢,因为它必须通过你的参数并在调用之前找到函数。它的缓慢程度取决于它有多少额外的计算来分摊这个开销。
> system.time(for(i in 1:1e6) do.call(rnorm, list(100)))
user system elapsed
13.55 0.00 13.58
> system.time(for(i in 1:1e6) rnorm(100))
user system elapsed
11.40 0.00 11.42
,而:
> system.time(for(i in 1:1e2) do.call(rnorm, list(1e6)))
user system elapsed
9.14 0.00 9.15
> system.time(for(i in 1:1e2) rnorm(1e6))
user system elapsed
9.14 0.00 9.14
此外,你的一些减速是由于你的正则表达式和其他字符串操作,这与do.call
本身的速度无关。虽然快速,因为它运行在一个小的输入,它仍然是不必要的复杂。为什么不这样做:
test <- function(distrib, ..., N=1e6)
lapply(seq(N), function(x) distrib(...))
test(rnorm, 100, 1, 10)
或者这个:
test <- function(call, N=1e6)
{
call <- substitute(call)
lapply(seq(N), function(...) eval.parent(call))
}
test(rnorm(100, 1, 10))