缺失的点 (`...`) 在哪里处理?
Where do absent dots (`...`) get processed?
如果我们查看参数列表中包含点 ...
的函数的主体,我们通常可以找到接收这些点参数的函数。
例如,我们可以在 sapply()
的正文中看到点参数传递给 lapply()
。
sapply
# function (X, FUN, ..., simplify = TRUE, USE.NAMES = TRUE)
# {
# FUN <- match.fun(FUN)
# answer <- lapply(X = X, FUN = FUN, ...)
# ## rest of function body
# }
# <bytecode: 0x000000000e05f0b0>
# environment: namespace:base>
然而,在lapply()
中,参数列表中有点...
,但函数体中没有。
lapply
# function (X, FUN, ...)
# {
# FUN <- match.fun(FUN)
# if (!is.vector(X) || is.object(X))
# X <- as.list(X)
# .Internal(lapply(X, FUN))
# }
# <bytecode: 0x0000000009414f08>
# <environment: namespace:base>
那么 lapply()
中的点 ...
参数在哪里得到处理? What/where他们被传给了吗?我们不能将它们传递给 match.fun()
。我假设它们被传递到 .Internal()
但当我没有看到它们被传递到函数体中的任何函数时,我看不出有任何理由起作用。
它们没有明确地传递给.Internal
,但我相信它们可供do_lapply
使用(在src/main/apply.c ) 通过动态作用域。作用域规则可能与通常的略有不同,因为 .Internal
是原始函数。
您可以看到 ...
(R_DotsSymbol
) 添加到 lapply
创建的函数调用中,因此它们可用于每个列表元素上的函数调用。 tmp
大致等同于 X[[i]]
而 R_fcall
大致等同于 FUN(X[[i]], ...)
.
SEXP tmp = PROTECT(LCONS(R_Bracket2Symbol,
LCONS(X, LCONS(isym, R_NilValue))));
SEXP R_fcall = PROTECT(LCONS(FUN,
LCONS(tmp, LCONS(R_DotsSymbol, R_NilValue))));
However, in lapply()
, there are dots ...
in the argument list but not in the function body.
但是他们在lapply
的body环境中!感兴趣的行是:
我们可以对 C++ 文件做一些非常相似的事情,我们可以在下面 Rcpp::sourceCpp
使用 Rf_lang3
而不是调用 ICONS
:
#include <Rcpp.h>
// [[Rcpp::export(rng = false)]]
SEXP to_simple_call_f_1(SEXP f, SEXP x, SEXP env){
SEXP fn = PROTECT(Rf_lang3(f, x, R_DotsSymbol)),
ans = Rf_eval(fn, env);
UNPROTECT(1);
return ans;
}
// [[Rcpp::export(rng = false)]]
SEXP to_simple_call_f_2(SEXP f, SEXP x, SEXP rho){
SEXP fn = PROTECT(Rf_lang3(f, x, R_DotsSymbol)),
ans = R_forceAndCall(fn, 1, rho);
UNPROTECT(1);
return ans;
}
// [[Rcpp::export(rng = false)]]
SEXP to_simple_call_f_3(SEXP f, SEXP x, SEXP rho){
SEXP fn = PROTECT(Rf_lang2(f, x)),
ans = Rf_eval(fn, rho);
UNPROTECT(1);
return ans;
}
我们现在可以看到:
f <- function(x, ...)
x * unlist(list(...))
w_wont_work <- function(x, ...)
to_simple_call_f_1(f = f, x = x, .GlobalEnv)
# first the solution that does not work (wrong env!)
a <- 3
b <- 2
w_wont_work(x, a, b)
#R> Error in to_simple_call_f_1(f = f, x = x, .GlobalEnv) :
#R> '...' used in an incorrect context
# now with the correct versions
w_1 <- function(x, ...)
to_simple_call_f_1(f = f, x = x, environment())
w_2 <- function(x, ...)
to_simple_call_f_2(f = f, x = x, environment())
# version that uses variables from the global environment instead
f3 <- function(x)
x * unlist(list(a, b))
w_3 <- function(x)
to_simple_call_f_3(f = f3, x = x, environment())
# check the functions
f (2, a, b)
#R> [1] 6 4
w_1(2, a, b)
#R> [1] 6 4
w_2(2, a, b)
#R> [1] 6 4
w_3(2)
#R> [1] 6 4
# almost runs in the same time
bench::mark(f(2, a, b), w_1(2, a, b), w_2(2, a, b), w_3(2), min_time = 1,
max_iterations = 1e9)
#R> # A tibble: 4 x 13
#R> expression min median `itr/sec` mem_alloc `gc/sec` n_itr n_gc total_time
#R> <bch:expr> <bch:tm> <bch:t> <dbl> <bch:byt> <dbl> <int> <dbl> <bch:tm>
#R> 1 f(2, a, b) 995ns 1.15µs 801375. 0B 25.1 733096 23 915ms
#R> 2 w_1(2, a, b) 1.96µs 2.25µs 420623. 6.77KB 26.0 388953 24 925ms
#R> 3 w_2(2, a, b) 1.93µs 2.21µs 424811. 6.77KB 25.9 393580 24 926ms
#R> 4 w_3(2) 1.76µs 1.99µs 474501. 15.23KB 22.5 442139 21 932ms
I presume they are passed into .Internal()
but I don't see any reason for this to work when I don't see them passed into any function in the function body.
注意 Rcpp 中的函数使用 .Call
而不是 .Internal
:
to_simple_call_f_1
#R> function (f, x, env)
#R> .Call(<pointer: 0x7fedc44cd1b0>, f, x, env)
于是,我通过了环境。所以我猜 .Internal
似乎通过了一个环境? ?.Internal
没有太大帮助。
如果我们查看参数列表中包含点 ...
的函数的主体,我们通常可以找到接收这些点参数的函数。
例如,我们可以在 sapply()
的正文中看到点参数传递给 lapply()
。
sapply
# function (X, FUN, ..., simplify = TRUE, USE.NAMES = TRUE)
# {
# FUN <- match.fun(FUN)
# answer <- lapply(X = X, FUN = FUN, ...)
# ## rest of function body
# }
# <bytecode: 0x000000000e05f0b0>
# environment: namespace:base>
然而,在lapply()
中,参数列表中有点...
,但函数体中没有。
lapply
# function (X, FUN, ...)
# {
# FUN <- match.fun(FUN)
# if (!is.vector(X) || is.object(X))
# X <- as.list(X)
# .Internal(lapply(X, FUN))
# }
# <bytecode: 0x0000000009414f08>
# <environment: namespace:base>
那么 lapply()
中的点 ...
参数在哪里得到处理? What/where他们被传给了吗?我们不能将它们传递给 match.fun()
。我假设它们被传递到 .Internal()
但当我没有看到它们被传递到函数体中的任何函数时,我看不出有任何理由起作用。
它们没有明确地传递给.Internal
,但我相信它们可供do_lapply
使用(在src/main/apply.c ) 通过动态作用域。作用域规则可能与通常的略有不同,因为 .Internal
是原始函数。
您可以看到 ...
(R_DotsSymbol
) 添加到 lapply
创建的函数调用中,因此它们可用于每个列表元素上的函数调用。 tmp
大致等同于 X[[i]]
而 R_fcall
大致等同于 FUN(X[[i]], ...)
.
SEXP tmp = PROTECT(LCONS(R_Bracket2Symbol,
LCONS(X, LCONS(isym, R_NilValue))));
SEXP R_fcall = PROTECT(LCONS(FUN,
LCONS(tmp, LCONS(R_DotsSymbol, R_NilValue))));
However, in
lapply()
, there are dots...
in the argument list but not in the function body.
但是他们在lapply
的body环境中!感兴趣的行是:
我们可以对 C++ 文件做一些非常相似的事情,我们可以在下面 Rcpp::sourceCpp
使用 Rf_lang3
而不是调用 ICONS
:
#include <Rcpp.h>
// [[Rcpp::export(rng = false)]]
SEXP to_simple_call_f_1(SEXP f, SEXP x, SEXP env){
SEXP fn = PROTECT(Rf_lang3(f, x, R_DotsSymbol)),
ans = Rf_eval(fn, env);
UNPROTECT(1);
return ans;
}
// [[Rcpp::export(rng = false)]]
SEXP to_simple_call_f_2(SEXP f, SEXP x, SEXP rho){
SEXP fn = PROTECT(Rf_lang3(f, x, R_DotsSymbol)),
ans = R_forceAndCall(fn, 1, rho);
UNPROTECT(1);
return ans;
}
// [[Rcpp::export(rng = false)]]
SEXP to_simple_call_f_3(SEXP f, SEXP x, SEXP rho){
SEXP fn = PROTECT(Rf_lang2(f, x)),
ans = Rf_eval(fn, rho);
UNPROTECT(1);
return ans;
}
我们现在可以看到:
f <- function(x, ...)
x * unlist(list(...))
w_wont_work <- function(x, ...)
to_simple_call_f_1(f = f, x = x, .GlobalEnv)
# first the solution that does not work (wrong env!)
a <- 3
b <- 2
w_wont_work(x, a, b)
#R> Error in to_simple_call_f_1(f = f, x = x, .GlobalEnv) :
#R> '...' used in an incorrect context
# now with the correct versions
w_1 <- function(x, ...)
to_simple_call_f_1(f = f, x = x, environment())
w_2 <- function(x, ...)
to_simple_call_f_2(f = f, x = x, environment())
# version that uses variables from the global environment instead
f3 <- function(x)
x * unlist(list(a, b))
w_3 <- function(x)
to_simple_call_f_3(f = f3, x = x, environment())
# check the functions
f (2, a, b)
#R> [1] 6 4
w_1(2, a, b)
#R> [1] 6 4
w_2(2, a, b)
#R> [1] 6 4
w_3(2)
#R> [1] 6 4
# almost runs in the same time
bench::mark(f(2, a, b), w_1(2, a, b), w_2(2, a, b), w_3(2), min_time = 1,
max_iterations = 1e9)
#R> # A tibble: 4 x 13
#R> expression min median `itr/sec` mem_alloc `gc/sec` n_itr n_gc total_time
#R> <bch:expr> <bch:tm> <bch:t> <dbl> <bch:byt> <dbl> <int> <dbl> <bch:tm>
#R> 1 f(2, a, b) 995ns 1.15µs 801375. 0B 25.1 733096 23 915ms
#R> 2 w_1(2, a, b) 1.96µs 2.25µs 420623. 6.77KB 26.0 388953 24 925ms
#R> 3 w_2(2, a, b) 1.93µs 2.21µs 424811. 6.77KB 25.9 393580 24 926ms
#R> 4 w_3(2) 1.76µs 1.99µs 474501. 15.23KB 22.5 442139 21 932ms
I presume they are passed into
.Internal()
but I don't see any reason for this to work when I don't see them passed into any function in the function body.
注意 Rcpp 中的函数使用 .Call
而不是 .Internal
:
to_simple_call_f_1
#R> function (f, x, env)
#R> .Call(<pointer: 0x7fedc44cd1b0>, f, x, env)
于是,我通过了环境。所以我猜 .Internal
似乎通过了一个环境? ?.Internal
没有太大帮助。