1 min read

Rcpp-1

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的語法:

  1. Rcpp的語法比較簡潔。尤其是在比較INTEGER_POINTER(retval)[0]retval[0], 這種在真正寫功能時最常用到的語法,我比較喜歡使用後者。雖然你也可以再宣告一個指標:int* retval_ptr = INTEGER_POINTER(retval) 來達到類似的效果,但是對我來說, 明明都是代表retval這個物件,卻需要宣告兩個變數就是彆扭。

  2. Rcpp可以使用更精確的型態。我認為比起SEXPNEW_INTEGERIntegerVector是更清楚的,而且也更簡潔。除了IntegerVector外,Rcpp之中也定義了許許多多的型態, 甚至連MatrixRobj(S4 object)和environment都有呢!

  3. 在物件導向的語法中我就可以依循RAII([Resource Acquisition Is Initialization] 2)的原則來寫code, 降低了memory leak等資源洩漏的可能,也免除了使用PROTECTUNPROTECT的困擾。

除了語法上吸引我之外,Rcpp還可以還可以和inline套件結合,直接在R內進行即時編譯;還可以和另一個套件sugar一起使用,再增加程式的執行效能。

安裝

Rcpp的安裝非常簡單,和一般的R套件相同。有興趣的使用者還可以額外安裝inline或sugar來玩。 Windows的使用者請額外安裝能編譯R的工具庫: Rtools ,並且記得到Rcpp的目錄中不要包含空白,否則用R CMD SHLIB編譯的時候會有錯誤。

編譯

  1. 使用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

  1. 使用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

  1. 在R內呼叫Rcpp的函數Rcpp:::SHLIB來編譯。

r Compile with Rcpp:::SHLIB Rcpp:::SHLIB("mysource.cpp")

在windows底下,方法2.我花了很久才測出來(網路上找到資料是說只要Rcpp和他的父目錄名稱有空白就會出錯)!後來我大部分是用方法1和3了。

參考資料