Rcpp 心得 – 簡介
概述
背景知識
就我所知,要能理解Rcpp的語法,你必須先對C++這個我個人認為最難學的語言先學到某種程度才行。根據Effective C++的作者Scott Meyers的看法,C++其實是下列四種程式語言的集合(難怪很難,一個打四個!!):
- C的特性 (指標、陣列等等)
- 物件導向
- STL 標準函式庫
- Template
Rcpp中大量的使用了後面三種,所以如果看不習慣Rcpp的使用者,可能得先回頭把C++後面三種的語法複習一下了。
介紹
[Rcpp] 1 是一個整合R和C++的library。
使用過R中的.Call
函數的人一定會對於處理R和C之間資料結構的轉換感到很煩人,而Rcpp給我的第一個印象就是他把這些重複性很高的轉換給包起來了!所以在使用Rcpp時使用者不需要再去撰寫諸如以下的程式碼:
c return a R integer vector with R API #include <R.h> #include <Rdefines.h> SEXP foo() { SEXP retval; PROTECT(retval = NEW_INTEGER(2)); INTEGER_POINTER(retval)[0] = 1; INTEGER_POINTER(retval)[1] = 2; UNPROTECT(1); return retval; }
在Rcpp中, 透過C++的物件導向和template語法可以用下列的語法得到相同的效果:
cpp return a R integer vector with Rcpp #include <Rcpp.h> RcppExport SEXP foo() { Rcpp::IntegerVector retval(2); retval[0] = 1; retval[1] = 2; return Rcpp::wrap( retval ); }
比較上面兩段語法後,我基於以下的理由比較喜歡Rcpp的語法:
Rcpp的語法比較簡潔。尤其是在比較
INTEGER_POINTER(retval)[0]
和retval[0]
, 這種在真正寫功能時最常用到的語法,我比較喜歡使用後者。雖然你也可以再宣告一個指標:int* retval_ptr = INTEGER_POINTER(retval)
來達到類似的效果,但是對我來說, 明明都是代表retval
這個物件,卻需要宣告兩個變數就是彆扭。Rcpp可以使用更精確的型態。我認為比起
SEXP
或NEW_INTEGER
,IntegerVector
是更清楚的,而且也更簡潔。除了IntegerVector
外,Rcpp之中也定義了許許多多的型態, 甚至連Matrix
、Robj
(S4 object)和environment
都有呢!在物件導向的語法中我就可以依循RAII([Resource Acquisition Is Initialization] 2)的原則來寫code, 降低了memory leak等資源洩漏的可能,也免除了使用
PROTECT
、UNPROTECT
的困擾。
除了語法上吸引我之外,Rcpp還可以還可以和inline套件結合,直接在R內進行即時編譯;還可以和另一個套件sugar一起使用,再增加程式的執行效能。
安裝
Rcpp的安裝非常簡單,和一般的R套件相同。有興趣的使用者還可以額外安裝inline或sugar來玩。 Windows的使用者請額外安裝能編譯R的工具庫: Rtools ,並且記得到Rcpp的目錄中不要包含空白,否則用R CMD SHLIB編譯的時候會有錯誤。
編譯
- 使用inline,這也是最簡單的方式。以下是一個取自Frequently Asked Questions about Rcpp的範例:
r Compile with R package "inline" fx <- cxxfunction(signature( x = "numeric" ), ' NumericVector xx(x); return wrap( std::accumulate( xx.begin(), xx.end(), 0.0));', plugin = "Rcpp") res <- fx( seq( 1, 10, by = 0.5 ) ) res
- 使用R CMD SHLIB的話則需要調整compiler的參數。以下的範例是在bash底下執行,是透過修改環境變數的方式。
bash Compile with R CMD SHLIB export PKG_LIBS=$(Rscript -e "Rcpp:::LdFlags()") export PKG_CXXFLAGS=$(Rscript -e "Rcpp::CxxFlags()") R CMD SHLIB myfile.cpp
- 在R內呼叫Rcpp的函數
Rcpp:::SHLIB
來編譯。
r Compile with Rcpp:::SHLIB Rcpp:::SHLIB("mysource.cpp")
在windows底下,方法2.我花了很久才測出來(網路上找到資料是說只要Rcpp和他的父目錄名稱有空白就會出錯)!後來我大部分是用方法1和3了。