この前紹介したGoogle Summer of Codeのやつですが、メンターがDirk Eddelbuettelさんで、この人はRcppというパッケージを作ってる人で僕はこのパッケージを使ったことがなかったので、使ってみようじゃないかというお話。以下を参考にしつつ。
対象はたぶんC拡張書いたことある人or書いてみたい人。
RのC拡張はめんどい&Rcppを使うと楽ができる
RのC拡張(Writing R Extentionを印刷して枕の横に置きましょう)を書くときの典型例として、例えばこんな感じのコードがよくある。SEXP ab; PROTECT(ab = allocVector(REALSXP, 2)); REAL(ab)[0] = 123.45; REAL(ab)[1] = 67.89; UNPROTECT(1);
Rさんが勝手にメモリ解放しないようにPROTECTしないといけないけど、それを忘れがちだったり、PROTECTした数を数えてUNPROTECTしないといけなかったりで、なかなか面倒。Rcppを使うと以下のように簡潔に書ける。
Rcpp::NumericVector ab(2); ab[0] = 123.45; ab[1] = 67.89;
他の型でも同様に簡単に書ける。Rcppを使わないでcharacter型のvectorを作ろうとすると
SEXP ab; PROTECT(ab = allocVector(STRSXP, 2)); SET_STRING_ELT( ab, 0, mkChar("foo") ); SET_STRING_ELT( ab, 1, mkChar("bar") ); UNPROTECT(1);
だが、Rcppを使うと
CharacterVector ab(2) ; ab[0] = "foo" ; ab[1] = "bar" ;
という感じで簡単に書ける。
C++からRへの変換 => wrap関数
まず、変換のルールから確認しよう。C++での型 | Rでの型 |
---|---|
プリミティブな型(intとかdouble) | 対応するRのvector |
std::string | character型のvector |
STLのコンテナ。std::vector |
Tがラップ可能なら、対応するもののlist |
std::map |
Tがラップ可能ならlistのnameにmapのキーの名前が入ったRのリストになる |
例として以下のようなC++のデータ構造は
std::vector< std::map<std::string,int> > v; std::map< std::string, int > m1; std::map< std::string, int > m2; m1["foo"] = 1; m1["bar"] = 2; m2["foo"] = 1; m2["bar"] = 2; m2["baz"] = 3; v.push_back( m1); v.push_back( m2); Rcpp::wrap( v );
Rだと
list( c( bar = 2L, foo = 1L), c( bar = 2L, baz = 3L, foo = 1L) )
のように変換される。
RからC++への変換 => as関数
Rcpp::Environment global = Rcpp::Environment::global_env(); std::vector<double> vx = global["x"]; std::map<std::string,std::string> map; map["foo"] = "oof"; map["bar"] = "rab"; global["y"] = map ;