尝试使用内部函数时出错

  • 本文关键字:出错 内部函数 r
  • 更新时间 :
  • 英文 :


我试图优化代码,并希望使用vapply.Internal实现,并得到一个我不理解的错误。(现在我将认真对待?.Internal的警告,"只有真正的R巫师才应该考虑使用这个函数"。并使用用户可见的vapply,但我想更好地理解错误。)

test <- rnorm(10000)
test_l <- as.list(test)
test_2 <- .Internal(vapply(test_l, (x) x^2, numeric(1), FALSE))
# Error: '...' used in an incorrect context

将其与用户可见的vapply:

代码进行比较
function (X, FUN, FUN.VALUE, ..., USE.NAMES = TRUE) 
{
FUN <- match.fun(FUN)
if (!is.vector(X) || is.object(X)) 
X <- as.list(X)
.Internal(vapply(X, FUN, FUN.VALUE, USE.NAMES))
}

谁能给我解释一下为什么这不起作用?

我的一些假设:

它是否在另一个命名空间中运行,而不是用户可见vapply的函数体,或者我是否出于其他原因调用用户可见函数而不是.Internal?

是因为内部vapply是一个特殊的内部,与其他内部不同的工作方式吗?

r -int手册[1]规定:

2.2特殊内部构件

也有特殊的内部函数:NextMethod, Recall, withVisible, cbind, rbind(允许分离)。

但是没有给出更多的细节。

谁能给我更多关于那些"特殊内部"的细节?

[1] https://cran.r-project.org/doc/manuals/R-ints.html g_t_002eInternal-vs -_002ePrimitive

C代码指的是R_DotsSymbol,即省略号。调用作用域没有省略号。它不能,因为它不是一个函数调用。

你可以使用标准的R代码重现错误,像这样:

foo <- function() {
return(...)
}
foo()
#Error in foo() : '...' used in an incorrect context

或者像这样使用.Internal调用vapply:

bar <- function(X, FUN, FUN.VALUE, USE.NAMES) {
.Internal(vapply(X, FUN, FUN.VALUE, USE.NAMES))
}
bar(test_l, (x) x^2, numeric(1), FALSE)
#Error in bar(test_l, function(x) x^2, numeric(1), FALSE) : 
#  '...' used in an incorrect context

这可以工作,因为...存在于调用作用域中:

baz <- function(X, FUN, FUN.VALUE, USE.NAMES, ...) {
.Internal(vapply(X, FUN, FUN.VALUE, USE.NAMES))
}
x <- baz(test_l, (x) x^2, numeric(1), FALSE)

如果跳过vapply的前几行,您将无法生成明显更快的代码。它们不是你的瓶颈。它可能有助于使用Rcpp实现vapply反复调用的函数,但真正的性能提升只能通过使用Rcpp实现整个循环来实现。对R闭包的调用是非常昂贵的,并且您希望在多次迭代的循环中避免调用它们。

Roland的分析在这里是正确的。有一个hack允许您在全局环境中获得省略号,但是它要求您传递给vapply的函数接受一个额外的未使用的参数:

`...` <- (function(...) get("..."))(y = 2)

现在你可以输入:

test <- rnorm(10)
test_l <- as.list(test)
.Internal(vapply(test_l, (x, y) x^2, numeric(1), FALSE))
#>  [1] 1.49370808 0.02969854 4.80764382 2.96895104 0.69506047 1.53488883
#>  [7] 0.12566700 1.27180579 0.08399010 0.02366073

但是,不建议使用。尽管从闭包内部调用.Internal的开销非常小,但正如Roland所说,这不会成为代码中的速率限制因素。

如果我们测量它:

microbenchmark::microbenchmark(
hack = {`...` <- (function(...) get("..."))(y = 2);
.Internal(vapply(test_l, (x, y) x^2, numeric(1), FALSE))},
standard = vapply(test_l, (x) x^2, numeric(1), USE.NAMES = FALSE))
#> Unit: microseconds
#>      expr min  lq   mean median  uq   max neval cld
#>      hack 9.0 9.3  9.690    9.6 9.8  17.8   100   a
#>  standard 6.7 7.0 15.261    7.1 7.2 817.9   100   a

我们可以看到,尽管黑客平均速度略快(只是由于标准版本中偶尔出现的异常值),但每次调用的速度大约为5微秒,因此如果调用该例程1000次,可能会节省5毫秒。当您考虑到调试这种方法的不透明性和难度时,它根本不值得。

创建于2022-11-08与reprex v2.0.2

最新更新