R是一个不错的脚本程序,每写一行代码都可以看到实时的输出结果。但是,它有一个很大的问题,就是在运行循环时效率太低。所以当大运算量的程序在R当中运行就显得非常的缓慢。解决办法就是将大运算量的部分交给其它语言去做,比如说C,我个人比较习惯用c/c++)。
下面就是一个非常简单的例子,教你如何用R来调用C中的程序。
首先打开一个文本编辑器,写下下面的代码:
/* * test.c * * * Created by Jianhong Ou on 3/21/12. * Copyright 2012 糗世界(qiuworld.com). All rights reserved. * */ #include <R.h> #include <Rmath.h> void add(double *a,double *b,int n,double *c){ *c=*a+*b; } void hello(int *n){ int i; for (i=0; i< *n; i++) { Rprintf("Hello, world!\n"); } } |
把它保存成test.c文件。其中,include了两个头文件,一个是R.h,一个是Rmath.h。前者是必须的,所有一切需要R调用的C程序都应该加上这一头文件。至于Rmath.h一般也是需要的,因为毕竟是用C来运算的,不可能只是写个hello world这个样子。可以注意到,原本的printf变成了Rprintf。这只是将printf的输出改成了R console而已。
接下来的一步是在terminal或者command当中运行命令,把c程序编译出来。
$ R CMD SHLIB test.c gcc -arch x86_64 -std=gnu99 -I/Library/Frameworks/R.framework/Resources/include -I/Library/Frameworks/R.framework/Resources/include/x86_64 -I/usr/local/include -fPIC -g -O2 -c test.c -o test.o gcc -arch x86_64 -std=gnu99 -dynamiclib -Wl,-headerpad_max_install_names -undefined dynamic_lookup -single_module -multiply_defined suppress -L/usr/local/lib -o test.so test.o -F/Library/Frameworks/R.framework/.. -framework R -Wl,-framework -Wl,CoreFoundation |
这时,你就可以在当前目录下看到test.o和test.so文件了。有一点我们需要注意的是,该命令会自己加上一些参数,比如说-arch等等。一般不需要修改。如果你需要修改,比如说输出的动态链接文件你希望是hello.so那你需要运行R CMD SHLIB -o hello.so test.c
接下来的工作就是进入R。注意,因为例子是x86_64体系下编译的,所以需要进入64位的R.在R当中,使用setwd设置工作目录为test.so所在的目录。而后运行
> dyn.load("test.so") > .C("hello",as.integer(3)) Hello, world! Hello, world! Hello, world! [[1]] [1] 3 |
成功!!等等,我们看到最后怎么出现了
[[1]] [1] 3
这是R调用C程序后会将所有的输入参数再回传回来。这一点非常重要。因为在整个调用过程中,c程序return的结果都会被抛弃,我们无法接受return回来的结果,只能通过传入参数地址来获得计算结果。所以,我们可以将C程序中所有的程序都写成void程序,然后通过传过一个地址来回收运算结果。例子中的add就是了。
> .C("add",as.double(2),as.double(5),as.double(0.0)) [[1]] [1] 2 [[2]] [1] 5 [[3]] [1] 7 > .C("add",as.double(2),as.double(5),as.double(0.0))[[3]] [1] 7 > add<-function(a,b){.C("add",as.double(a),as.double(b),as.double(0.0))[[3]]} > add(2,3) [1] 5 |
这条语句
.C(“add”,as.double(2),as.double(5),as.double(0.0))
我的结果是
> .C(“add”,as.double(2),as.double(5),as.double(0.0))
[[1]]
[1] 2
[[2]]
[1] 5
[[3]]
[1] 0
和你的不一样,不知道怎么样才能得到你的结果?
你的add函数的原代码是什么样子的?看了就明白了。我猜可能是指针问题。
你好,
请问有什么方法可以在R中查看C的源码?
谢谢。
下载源代码,进行查看。
在test.c中有个小错误,导致
> .C(“add”,as.double(2),as.double(5),as.double(0.0))
[[1]]
[1] 2
[[2]]
[1] 5
[[3]]
[1] 0
错误在:
void add(double *a,double *b,int n,double *c){
*c=*a+*b;
}
把int n,删掉,就会得到正确的答案!
谢谢指正。