我想在C/C++中复制以下R函数:
fn1 = function(a, b) eval(a, b)
fn1(substitute(a*2), list(a = 1))
#[1] 2
Run Code Online (Sandbox Code Playgroud)
我的第一次尝试导致错误(有时在崩溃中),可能是因为我没有从列表对象获取环境(我查看了R源代码,并且它使用了一堆内部函数)我不认为我可以使用的点,我认为这是Rf_eval想要的而不是对象本身.
require(Rcpp)
require(inline)
fn2 = cxxfunction(signature(x = "SEXP", y = "SEXP"),
'return Rf_eval(x, y);')
fn2(substitute(a*2), list(a = 1))
# error, object 'a' not found
Run Code Online (Sandbox Code Playgroud)
另一种尝试是尝试调用基数R eval,这也给出了同样的错误:
require(Rcpp)
require(inline)
fn3 = cxxfunction(signature(x = "SEXP", y = "SEXP"),
'Function base_eval("eval"); return base_eval(x, y);',
plugin = 'Rcpp')
fn3(substitute(a*2), list(a = 1))
# again, object 'a' not found
Run Code Online (Sandbox Code Playgroud)
每种方法都缺少什么,我怎样才能使它们都有效?
内部Rf_eval期望将环境作为其第二个参数.列表不是环境.您可以list2env在R端使用将列表转换为环境.因此,将此内容放在单独的.cpp文件中:
#include <Rcpp.h>
using namespace Rcpp ;
// [[Rcpp::export]]
SEXP fn_impl( Language call, List env){
return Rf_eval( call, env ) ;
}
Run Code Online (Sandbox Code Playgroud)
sourceCpp 该文件并创建一个包装器R函数以方便创建环境:
sourceCpp( "fn.cpp")
fn <- function(call, list){
fn_impl( call, list2env(list) )
}
fn(substitute(a*2), list(a = 1))
Run Code Online (Sandbox Code Playgroud)
如果您不想创建环境,这是一项更多工作,但您可以使用C++导航调用并替换自己.我们在dplyr实施混合评估方面做了很多工作.
因为fn3,我认为这是关于.Call评估其论点.看看如果eval用这个函数替换会发生什么:
beval <- function(...){ print(match.call()); eval(...) }
Run Code Online (Sandbox Code Playgroud)
这样你就可以看到函数的调用方式:
fn3 = cxxfunction(signature(x = "SEXP", y = "SEXP"),
'Function base_eval("beval"); return base_eval(x, y);',
plugin = 'Rcpp')
fn3(substitute(a*2), list(a = 1))
# (function (...)
# {
# print(match.call())
# eval(...)
# })(a * 2, list(a = 1))
Run Code Online (Sandbox Code Playgroud)
您需要非标准评估.一种方法是发送未评估的参数列表.
dots <- function(...) {
eval(substitute(alist(...)))
}
fn <- function(...){
args <- dots(...)
fn_impl(args)
}
Run Code Online (Sandbox Code Playgroud)
您在C++层使用它来构造对其的调用eval和评估:
#include <Rcpp.h>
using namespace Rcpp ;
// [[Rcpp::export]]
SEXP fn_impl( List args){
Language call = args[0] ;
List data = args[1] ;
// now construct the call to eval:
Language eval_call( "eval", call, data ) ;
// and evaluate it
return Rf_eval( eval_call, R_GlobalEnv ) ;
}
Run Code Online (Sandbox Code Playgroud)