【问题标题】:how to call a function created in Rcpp from another Rccp function如何从另一个 Rccp 函数调用在 Rcpp 中创建的函数
【发布时间】:2021-07-31 20:12:32
【问题描述】:

我想在函数 h_evap 中运行一个 Rcpp 函数 esat。两者都在一个通用的 .cpp 文件中,我在 Rstudio 中使用 sourceCPP 执行它。这是代码。 esat 和 h_evap 都被创建并且 esat 工作正常。但是 h_evap 给了我输出

> esat(42)
[1] 256.7082
> h_evap(42)
Error in h_evap(42) : 
  Not compatible with requested type: [type=closure; target=double].

我怀疑问题在于我如何尝试从全局环境访问 esat,但不知道如何调用 esat 来获取输出值而不是闭包。

#include <Rcpp.h>
using namespace Rcpp;

// [[Rcpp::export]]

    NumericVector esat(NumericVector Tk) {
      NumericVector esat_out(Tk.size(), NAN);
      for (size_t i=0; i<Tk.size(); i++) {
        esat_out[i] = 6.1121 * Tk[i];
      }
      return esat_out;
    }
    
    // [[Rcpp::export]]
    NumericVector h_evap(NumericVector Tk) {
      Environment env = Environment::global_env();
      NumericVector f_esat = env["esat"];
      NumericVector h_evap_out(Tk.size(), NAN);
      for (size_t i=0; i<Tk.size(); i++) {
        
        h_evap_out[i] = (313.15 - Tk[i]);
        h_evap_out[i] = h_evap_out[i] + f_esat(Tk[i]);
      }

  return h_evap_out;
}

/*** R
h_evap(42)
*/

另一种方法是使用 cppFunction。我已经尝试过了,但仍然得到这个 Rcpp 新手不清楚的错误。这是代码

library(Rcpp)

cppFunction('NumericVector esat(NumericVector Tk) {
  NumericVector esat_out(Tk.size(), NAN);
  for (size_t i=0; i<Tk.size(); i++) {
    esat_out[i] = 6.1121 * Tk[i];
  }
  return esat_out;
}')

cppFunction('NumericVector h_evap(NumericVector Tk) {
   NumericVector h_evap_out(Tk.size(), NAN);
  for (size_t i=0; i<Tk.size(); i++) {
    h_evap_out[i] = esat(Tk[i]);
  }
  return h_evap_out;
}')

esat 编译得很好。 h_evap 返回一个错误信息我不清楚...

【问题讨论】:

  • 该行不应该是Function f_esat = env["esat"];
  • 有不同的工具用于不同的目的。 cppFunction() 有其用途,sourceCpp() 也有其用途。但最终你想要一个包裹。而且它真的并不难。
  • 您之前可能遇到过R CMD buildR CMD INSTALL,或者如果您没有遇到过它们在RStudio 中的等效按钮,或者devtools 成语,或者这些天人们为避免使用的其他任何东西记录的用法:) 开玩笑,我通常只是用包名(没有额外的参数)调用Rcpp.package.skeleton(),复制源文件并从那里获取。在手动构建中,您需要compileAttributes(),如果您使用 RStudio,它会为您完成。我们在许多教程和研讨会中记录了这一点,也许一些现有的幻灯片可以提供帮助。
  • 当然,您可以选择将自己描绘成您喜欢的任何角落。但是我们的许多 Rcpp 教程也展示了如何在 RStudio 中使用 Rcpp 构建包。我将这些作为免费服务提供,如果它们不适合您,则有退款保证,您可以尝试其他方法。世界很大。
  • 您遇到的另一个问题是 esat(NumericVector Tk) 需要一个向量,但您尝试使用标量调用:... + f_esat(Tk[i]); 这行不通。我经常发现在尝试跑步或冲刺之前步行会有所帮助。尝试循序渐进地做事。

标签: r rcpp


【解决方案1】:

稍微重写您的文件以避免通过中间 R 函数调用 C++,这(通常)是一个坏主意,而且几乎总是对性能造成不必要的沉重负担。

正如您在同一文件中定义一个有效的 C++ 函数在使用它之前(这样您就不需要签名来将其声明为例如头文件会为你做的)可以简单地调用它。

我还更改了循环索引变量以在编译期间消除一个警告,并且,当我这样做时,删除了 using namespace Rcpp; 并切换到使用命名空间的显式调用,这更明确,更“安全”一点在更大的代码库中。

编辑: 由于您的循环实际上对循环索引是不变的, 我们可以将代码重写为 vectorized 调用,这样更短、更简单、更快且更易于推理。 (当然,也可以从 R 完成...)

代码

#include <Rcpp.h>

// [[Rcpp::export]]
Rcpp::NumericVector esat(Rcpp::NumericVector Tk) {
    Rcpp::NumericVector esat_out(Tk.size(), NAN);
    for (R_xlen_t i=0; i<Tk.size(); i++) {
        esat_out[i] = 6.1121 * Tk[i];
    }
    return esat_out;
}

// [[Rcpp::export]]
Rcpp::NumericVector h_evap(Rcpp::NumericVector Tk) {
    Rcpp::NumericVector h_evap_out(Tk.size(), NAN);
    Rcpp::NumericVector f_out = esat(Tk);
    for (R_xlen_t i=0; i<Tk.size(); i++) {
        h_evap_out[i] = (313.15 - Tk[i]);
        h_evap_out[i] = h_evap_out[i] + f_out[i];
    }
    return h_evap_out;
}

// [[Rcpp::export]]
Rcpp::NumericVector esatV(Rcpp::NumericVector Tk) {
    Rcpp::NumericVector esat_out = 6.1121 * Tk;
    return esat_out;
}

// [[Rcpp::export]]
Rcpp::NumericVector h_evapV(Rcpp::NumericVector Tk) {
  Rcpp::NumericVector f_out = esatV(Tk);
  Rcpp::NumericVector h_evap_out = 313.15 - Tk + f_out;
  return h_evap_out;
}

/*** R
esat(42)
h_evap(42)
esatV(42)
h_evapV(42)
*/

用法

> Rcpp::sourceCpp("~/git/stackoverflow/68605528/answer.cpp")

> esat(42)
[1] 256.708

> h_evap(42)
[1] 527.858

> esatV(42)
[1] 256.708

> h_evapV(42)
[1] 527.858
> 

【讨论】:

    【解决方案2】:

    通过对各种堆栈溢出 Q 和 As 的一些明智研究以及 Dirk 的一些关键提示,以下代码似乎可以满足我的需求。两个关键点 - 使用 Function 并将 esat 的输出写入 NumericVector 并将其读入 h_evap。我还在做包...

    #include <Rcpp.h>
    using namespace Rcpp;
    
    // [[Rcpp::export]]
    
    NumericVector esat(NumericVector Tk) {
      NumericVector esat_out(Tk.size(), NAN);
      for (size_t i=0; i<Tk.size(); i++) {
        esat_out[i] = 6.1121 * Tk[i];
      }
      return esat_out;
    }
    
    // [[Rcpp::export]]
    NumericVector h_evap(NumericVector Tk) {
      Environment env = Environment::global_env();
      Function f_esat = env["esat"];
      NumericVector h_evap_out(Tk.size(), NAN);
      NumericVector f_out = f_esat(Tk);
      for (size_t i=0; i<Tk.size(); i++) {
    
        h_evap_out[i] = (313.15 - Tk[i]);
        h_evap_out[i] = h_evap_out[i] + f_out[i];
      }
    
      return h_evap_out;
    }
    
    /*** R
    esat(42)
    h_evap(42)
    */
    

    【讨论】:

    • 不要通过Rcpp::Environment 致电esat。这将扼杀大量的性能。真的。根据您的问题,您确实想要一个调用 C++ 函数的 C++ 函数而不通过 R 函数
    • 不确定您是否看到了我的答案,但这里的答案在两个方面有所改进(这有助于拼出完整且可重复的示例)。
    • 我不仅在今天早上看到了它,而且在我更大的代码中实现了它的元素,并意识到要做一些其他的事情,这将使我的代码更清晰,希望更快。现在所有函数都被向量化了,cpp 文件中函数的顺序已经重做,以消除从全局环境中读取的需要,包括我一直在使用的一些常量。非常感谢您的回答!我会说,看到那里有多少潜在的力量以及我对访问它知之甚少,这有点令人沮丧。此外,摆脱了命名空间 Rcpp。
    • 太棒了。与往常一样,这一切在复杂性和容量方面都有些压倒性。所以一次拿东西。小步骤也有帮助,尤其是在添加其中一些步骤时。
    • Net net 我们都被一个简单的逻辑弄糊涂了/因此编译器错误(你的'标量向量预期')导致你的初始编译失败。婴儿步。一次明智地进行一项检查的最佳方法...
    猜你喜欢
    • 2013-11-13
    • 2014-06-25
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-06-14
    • 1970-01-01
    相关资源
    最近更新 更多