学习如何编写用于R的C代码的最佳资源是什么?我知道R扩展的系统和外语接口部分,但我发现它很难.编写用于R的C代码有哪些好的资源(在线和离线)?
为了澄清,我不想学习如何编写C代码,我想学习如何更好地集成R和C.例如,我如何从C整数向量转换为R整数向量(反之亦然)或者从C标量到R向量?
Dir*_*tel 68
好吧有好老使用来源,卢克!--- R本身有很多(非常有效的)可以学习的C代码,CRAN有数百个包,有些来自你信任的作者.这提供了真实的,经过测试的例子来研究和适应.
但正如Josh所怀疑的那样,我更倾向于C++,因此更倾向于Rcpp.它也有很多例子.
编辑:有两本我觉得有用的书:
也就是说,John喜欢Rcpp(和贡献),因为他发现R对象和C++对象(通过Rcpp)之间的匹配非常自然 - 而ReferenceClasses帮助那里.
Edit 2: With Hadley's refocussed question, I very strongly urge you to consider C++. There is so much boilerplate nonsense you have to do with C---very tedious and very avoidable. Have a look at the Rcpp-introduction vignette. Another simple example is this blog post where I show that instead of worrying about 10% differences (in one of the Radford Neal examples) we can get eightyfold increases with C++ (on what is of course a contrived example).
编辑3:很复杂的是,您可能会遇到C++错误,这些错误很明显很难理解.但是,只使用Rcpp而不是扩展它,你几乎不需要它.虽然这个成本是不可否认的,它远远由黯然失色利益的简单的代码,样板少,无保护/取消保护,没有内存管理等页.道格·贝茨就在昨天表示,他认为C++和RCPP要更喜欢写[R而不是写C++.YMMV等等.
Rom*_*ois 54
哈德利
你绝对可以编写类似于C代码的C++代码.
我理解你对C++比C更复杂的说法.如果你想掌握一切:对象,模板,STL,模板元编程等等...大多数人不需要这些东西,只能依赖别人它.Rcpp的实施非常复杂,但仅仅因为你不知道你的冰箱是如何工作的,这并不意味着你不能打开门并抓住新鲜的牛奶......
从你对R的许多贡献中,让我感到震惊的是你发现R有点乏味(数据处理,图形,字符串操作等等).好用R的内部C API为更多的惊喜做好准备.这非常繁琐.
我不时阅读R-exts或R-ints手册.这有帮助.但大多数时候,当我真的想要了解某些内容时,我会进入R源代码,也会进入例如Simon编写的软件包源代码(通常有许多东西要学习).
Rcpp旨在使API的这些繁琐方面消失.
基于一些例子,你可以自己判断你发现的更复杂,混淆等等.此函数使用C API创建字符向量:
SEXP foobar(){
SEXP ab;
PROTECT(ab = allocVector(STRSXP, 2));
SET_STRING_ELT( ab, 0, mkChar("foo") );
SET_STRING_ELT( ab, 1, mkChar("bar") );
UNPROTECT(1);
}
Run Code Online (Sandbox Code Playgroud)
使用Rcpp,您可以编写相同的函数:
SEXP foobar(){
return Rcpp::CharacterVector::create( "foo", "bar" ) ;
}
Run Code Online (Sandbox Code Playgroud)
要么:
SEXP foobar(){
Rcpp::CharacterVector res(2) ;
res[0] = "foo" ;
res[1] = "bar" ;
return res ;
}
Run Code Online (Sandbox Code Playgroud)
正如德克所说,在几个小插曲中还有其他例子.我们通常也会将人们指向我们的单元测试,因为他们每个测试代码的一个非常具体的部分,并且有点自我解释.
我显然有偏见,但我建议熟悉Rcpp,而不是学习R的C API,然后如果有些东西不清楚或者看起来不适合Rcpp那么就来到邮件列表.
无论如何,销售宣传结束.
我想这一切都取决于你最终要写的代码类型.
罗曼
Rom*_*ois 28
@hadley:遗憾的是,我没有特定的资源来帮助您开始使用C++.我从Scott Meyers的书中找到了它(有效的C++,更有效的C++等等),但这些并不是人们可以称之为入门的.
我们几乎只使用.Call接口来调用C++代码.规则很简单:
所以.Call函数在某些头文件中声明如下:
#include <Rcpp.h>
RcppExport SEXP foo( SEXP x1, SEXP x2 ) ;
Run Code Online (Sandbox Code Playgroud)
并在.cpp文件中实现如下:
SEXP foo( SEXP x1, SEXP x2 ){
...
}
Run Code Online (Sandbox Code Playgroud)
关于使用Rcpp的R API还有很多东西需要了解.
大多数人只想处理Rcpp中的数字向量.您可以使用NumericVector类执行此操作.有几种方法可以创建数字向量:
从您从R传递下来的现有对象:
SEXP foo( SEXP x_) {
Rcpp::NumericVector x( x_ ) ;
...
}
Run Code Online (Sandbox Code Playgroud)
使用:: create static function给定值:
Rcpp::NumericVector x = Rcpp::NumericVector::create( 1.0, 2.0, 3.0 ) ;
Rcpp::NumericVector x = Rcpp::NumericVector::create(
_["a"] = 1.0,
_["b"] = 2.0,
_["c"] = 3
) ;
Run Code Online (Sandbox Code Playgroud)
给定大小:
Rcpp::NumericVector x( 10 ) ; // filled with 0.0
Rcpp::NumericVector x( 10, 2.0 ) ; // filled with 2.0
Run Code Online (Sandbox Code Playgroud)
然后,一旦你有了一个向量,最有用的是从中提取一个元素.这是通过operator []完成的,基于0的索引,所以例如数值向量的求和值如下所示:
SEXP sum( SEXP x_ ){
Rcpp::NumericVector x(x_) ;
double res = 0.0 ;
for( int i=0; i<x.size(), i++){
res += x[i] ;
}
return Rcpp::wrap( res ) ;
}
Run Code Online (Sandbox Code Playgroud)
但是使用Rcpp糖,我们现在可以做得更好:
using namespace Rcpp ;
SEXP sum( SEXP x_ ){
NumericVector x(x_) ;
double res = sum( x ) ;
return wrap( res ) ;
}
Run Code Online (Sandbox Code Playgroud)
正如我之前所说,这完全取决于你想要编写什么类型的代码.看看人们在依赖Rcpp的软件包中做了什么,检查小插图,单元测试,在邮件列表上回到我们这里.我们总是很乐意提供帮助.
Rom*_*ois 19
@jbremnant:没错.Rcpp类实现了接近RAII模式的东西.创建Rcpp对象时,构造函数会采取适当的措施来确保底层R对象(SEXP)不受垃圾收集器的影响.析构函数撤销保护.这在Rcpp-intrduction小插图中有解释.底层实现依赖于R API函数R_PreserveObject和R_ReleaseObject
由于C++封装,确实存在性能损失.我们试图通过内联等方式将其保持在最低限度......惩罚很小,当您考虑到编写和维护代码所需的时间增益时,它就不那么重要了.
从Rcpp类调用R函数函数比使用C api直接调用eval要慢.这是因为我们采取预防措施并将函数调用包装到tryCatch块中,以便捕获R错误并将它们提升为C++异常,以便可以使用C++中的标准try/catch来处理它们.
大多数人都想使用矢量(特别是NumericVector),这个类的惩罚很小.examples/ConvolveBenchmarks目录包含来自R-exts的臭名昭着的卷积函数的几种变体,并且晕图具有基准测试结果.事实证明,Rcpp比使用R API的基准代码更快.