【问题标题】:eval and substitute in C/C++C/C++ 中的评估和替换
【发布时间】:2013-11-05 04:11:07
【问题描述】:

我想在 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

每种方法都缺少什么以及如何使它们都起作用?

【问题讨论】:

    标签: c r rcpp


    【解决方案1】:

    内部Rf_eval 期望环境作为其第二个参数。列表不是环境。您可以在 R 端使用 list2env 将您的列表转换为环境。因此,将此内容放在单独的 .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 ) ;
    }
    

    【讨论】:

    • 谢谢,这主要回答了问题的第一部分 - 知道为什么第二个 (fn3) 不起作用吗?
    • 这是由于我们如何实现Function::operator()。见github.com/RcppTeam/Rcpp/blob/master/inst/include/Rcpp/…
    • 抱歉,您能详细说明一下吗?
    • 实际上,这是关于 .Call 如何评估其参数的。我已经在我的答案中添加了肉。
    • 这很有趣,谢谢!根据您的上述解释,以下 - fn3(substitute(substitute(a*2)), list(a = 1)) 是另一种解决方法
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2017-04-17
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多