R工具布局,用.Call的挪用方法更机动地写R-package
固然R的利用资料许多也很齐备,但R的开拓资料却少得可怜,写一个能管点用途的package也得大费周章。于是蜀中无上将、廖化当先锋,官方文档那区区130页不齐备而又艰涩的<Writing R Extensions>就成了红宝书,每天抱着啃,啃得呕心呖血再加上翻着几个现成package的源代码看,终有小小成。
R与C的挪用接口有好几种,以前先容过的.C形式是一种,另外尚有.Call形式和.External形式。.Call利用更为遍及,成果也比.C形式大许多,因为利用它可以直接操纵R的数据布局,可以实此刻C/C++中操纵R的数据及挪用R的函数,然后封装为包供R利用。
R的所有数据范例(无论数据或类或函数)都是工具,都统必然义为布局SEXPREC,由指针SEXP(不要想歪,我也不大白作者为何要取这个名字)所指向。这个布局在Rinternal.h中界说为:
typedef struct SEXPREC {
SEXPREC_HEADER;
union {
struct primsxp_struct primsxp;
struct symsxp_struct symsxp;
struct listsxp_struct listsxp;
struct envsxp_struct envsxp;
struct closxp_struct closxp;
struct promsxp_struct promsxp;
} u;
} SEXPREC, *SEXP;
它包括一个HEADER及一个UNION,HEADER又是由如下宏界说:
#define SEXPREC_HEADER \
struct sxpinfo_struct sxpinfo; \
struct SEXPREC *attrib; \
struct SEXPREC *gengc_next_node, *gengc_prev_node
它包括一个32位的sxpinfo的信息头,一个指向属性的指针,两个指向前后节点的指针组成双向链表。
sxpinfo这个信息头的界说如下:
struct sxpinfo_struct {
SEXPTYPE type : 5;
unsigned int obj : 1;
unsigned int named : 2;
unsigned int gp : 16;
unsigned int mark : 1;
unsigned int debug : 1;
unsigned int trace : 1; /* functions and memory tracing */
unsigned int spare : 1; /* currently unused */
unsigned int gcgen : 1; /* old generation number */
unsigned int gccls : 3; /* node class */
}; /* Tot: 32 */
简朴说几个主要字段的浸染:type暗示数据范例,如数字型、字符型、列表型等等;named节制引用时拷贝与否;mark为垃圾接纳所用(GC)。
利用.C挪用方法来做R的扩展是最简朴的方法,因为不需要剖析SEXP这些巨大的R布局,只需要把数据拆解为一个个向量通报给C函数即可,详情拜见我以前的描写。假如.C可以实现的成果,倒不须要必然要折腾到.Call,因为后者要牵涉到许多的R API,这些API还都没有文档说明其浸染及挪用方法,只能按照<Writing R Extensions>以及参考一些包的用法来揣摩着利用。不外长处是.Call的成果更富厚并且不变,.C方法的指针纵横交织,极易使措施瓦解。
这里先用官方文档里的一个例子拿出来加以讲解,叙述个概略流程,今后实践深入再团结一些履向来谈谈能力。
在C里处理惩罚R工具,以下是一个计较外积的例子:
在C文件中的源代码为:
#include <R.h>
#include <Rinternals.h> // 所有R API都界说在这里,只惋惜没有太多说明,需要细细看
SEXP out(SEXP x, SEXP y) // 无论参数,照旧返回值,都要界说为SEXP指针范例
{
int i, j, nx, ny;
double tmp, *rx = REAL(x), *ry = REAL(y), *rans; // REAL函数取得SEXP布局中的数据指针(假如是整型,则用INTEGER),以利便对数据的操纵
SEXP ans;
nx = length(x); ny = length(y);
PROTECT(ans = allocMatrix(REALSXP, nx, ny)); // 分派空间,返回一个SEXP指向的工具,REALSXP是指实数范例,这里生成一个实数矩阵。PROTECT掩护该内存不被垃圾回归机制接纳。
rans = REAL(ans);
for(i = 0; i < nx; i++) {
tmp = rx[i];
for(j = 0; j < ny; j++)
rans[i + nx*j] = tmp * ry[j];
}
UNPROTECT(1); // 放弃对以上PROTECT声明内存段的掩护,参数1指示放弃1次,次数必需跟上面利用PROTECT次数一致。
return(ans);
}
如果文件存储为out.c,则用R CMD SHLIB out.c呼吁可以把源代码编译为out.so共享库。然后在R里利用如下代码即可利用:
a=1:3
b=4:6
dyn.load(“out.so”)
.Call(“out”, a, b)