1. Rccp简明教程(关于R与C++的混合使用方法)

Rcpp简明指南: R调用C++

张金龙

注: 本文需要读者了解:

1) R函数的基本知识

2 编写R程序包

3) C++的基础知识, 包括 类, 以及namespace等。

为什么要用R调用C++函数? 原因可能有以下几点:

(1) R开发虽然很快, 涉及到一些循环的时候, 执行效率却常常相当于C++1/100,甚至更慢

(2) C++中有一些已经开发好的库, 人们不希望再重写一遍R的代码。 而是最好通过编译之后直接调用。

Rcpp程序包为用R调用C++代码提供了方便。

用户直接在R的源代码src文件夹下加入cpph文件, rtools即可对C++文件进行编译。 但是为了从Rconsole能够直接调用cpp写的函数, 还必须对cpp的函数进行修改。

修改需要遵循以下原则:

所有从R输入到cpp的对象, 都是SEXP

cpp返回到R的对象也必须是SEXP

3 C++中, 返回值如为数值, 可以用wrap()帮忙, 直接返回为SEXP的对象, 实现R的调用

换句话说, 只有加了SEXP这样一个标签,C++函数在 RC++中才都能识别。 Rcpp提供了SEXP处理的响应的类, 使得两者之间的数据传输变得容易。 

以下是用R调用C++的一个简单例子:

如果在windows中要使用Rcpp, 则需要

安装Rcpp程序包,install.packages("Rcpp")

安装Rtools, 并配制好启动路径。电脑>属性>高级系统设置>高级>环境变量>系统变量>路径

R console 中运行 library(Rcpp) , 之后调用 Rcpp.package.skeleton函数,

Rcpp.package.skeleton( "test" )

在生成的R包 skeleton中, 可以找到rcpp_hello_world.cpp文件, 可以尝试添加几个函数, 修改成如下样式 (注意以下代码为C++文件,扩展名为cpp)

////////////////////// C++文件开始

#include "rcpp_hello_world.h"
#include <Rcpp.h>
using namespace Rcpp;
using namespace std;
// 注意 #include <Rcpp.h> 是调用Rcpp的必要条件
RcppExport SEXP intVec1a(SEXP vec) {
   Rcpp::NumericVector vec2(vec);
   int prod = 1;
   for (int i=0; i<vec2.size(); i++) {
       prod *= vec2[i];
   }
   return (wrap(prod));
}

// RcppExport 是为了在载入R包后, 在Namespace内内能够找到这个C++的函数

//第一个SEXP声明返回值的类型

//第二个SEXP, 声明vec变量为SEXP的类型,也就是说, 它可以是从R直接传入

// Rcpp::NumericVector vec2(vec); 意思 是将 vec转换为 vec2, 从SEXP转换为 NumericVector, NumericVectorRcpp下的一个类, 在C++的函数中可以识别和调用。 后面的代码就和普通的C++没有什么分别了。

//return (wrap(prod)); 由于我们希望在R中直接可以得到该函数的运行结果, 因此, 返回值也需要转换为SEXP类型。 wrap函数提供了一种便捷的方式。 简单的类型, 如int, double等, 只需要直接调用wrap即可。 
RcppExport SEXP Eccentricity(SEXP JD)

 double JD3  = Rcpp::as<double>(JD);
 double T = (JD3 - 2451545.0) / 36525.0;
 double Tsquared = T*T;
 return (wrap(1 - 0.002516*T - 0.0000074*Tsquared));
}
// Rcpp::as<double>(JD); 表示调用Rcpp内的方法, 将类型为SEXPJD, 转换为double类型, 并赋值給double JD3
RcppExport SEXP expfun(SEXP x){
double x2 = Rcpp::as<double>(x);
double ans=1.0, term=1.0, eps=1e-16;
int n=0;
while (fabs(term)>eps){
   n++;
   term = term * x2 / n;
   ans = ans + term;
}
  return(wrap(ans));
}
/////////////  C++文件结束
R程序包test的骨架进行修改后, 即可开始安装,

windows下, 在 开始, cmd, 输入 Rcmd INSTALL test, 安装test程序包

之后转入 R console, library(test)

即可加载响应的动态链接库dll文件

只是此时调用dll中的文件, 还稍微有些不方便。 此时调用dll中函数的方法如下

.Call('Eccentricity', JD= 2314543)

.Call('intVec1a', vec= c(3, 4, 5))

.Call('expfun', x=3)

不过每次使用.Call函数, 仍然让人觉得不太满意。为了需要写一个Rwrapper function例如

expfunR <- function(x){

   return( .Call('expfun', x= x))

}

EccentricityR <- function(JD){

   return( .Call('Eccentricity', JD= JD))

}

intVecR <- function(x){
   return(.Call('intVec1a', vec= x))
}

调用方式同R函数,

expfunR(2)

EccentricityR(2314543)

intVecR(c(2,3,4))

这时候就和一般的R函数没有区别了。 

(2013.11.18 星期一 哈尔滨初次暴雪)

你可能感兴趣的:(1. Rccp简明教程(关于R与C++的混合使用方法))