5

我想在 C/C++ 中复制以下 R 函数:

fn1 = function(a, b) eval(a, b)

fn1(substitute(a*2), list(a = 1))
#[1] 2

我的前几次尝试导致错误(有时会导致崩溃),可能是因为我没有从列表对象中获取环境(我查看了 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

另一种尝试是尝试调用 base 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

每种方法都缺少什么,我怎样才能使它们都起作用?

4

1 回答 1

7

内部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 ) ;
}

sourceCpp该文件并创建一个包装器 R 函数以方便创建环境:

sourceCpp( "fn.cpp")
fn <- function(call, list){
    fn_impl( call, list2env(list) )
}

fn(substitute(a*2), list(a = 1))

如果您不想创建环境,这需要更多的工作,但您可以在 C++ 中导航调用并自己替换。我们在dplyr实施混合评估方面做了很多工作。

对于fn3,我认为这是关于.Call评估其论点。看看如果你用eval这个函数替换会发生什么:

beval <- function(...){ print(match.call()); eval(...) }

这样您就可以看到该函数的调用方式:

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))

你需要非标准的评估。一种方法是发送未评估参数的列表。

dots <- function(...) {
    eval(substitute(alist(...)))
}

fn <- function(...){
    args <- dots(...)
    fn_impl(args)
}

您在 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 ) ;
}
于 2013-11-01T19:22:51.060 回答