第6章 その他

6.1 パッケージ内で日本語を使う

Rのパッケージに非ASCII文字を含めることは推奨されない。だが可能だ。

具体的には、DESCRIPTIONEncodingを指定しつつ、

  1. \uXXXXのエスケープを使う
  2. データに含める
  3. メッセージ翻訳の仕組みを使う

のいずれかの手を使う。

6.1.1 DESCRIPTION

EncodingフィールドにUTF-8を指定する。これはおそらくいずれの手を使うにしても必要なはず。

Encoding: UTF-8

あと、Writing R Extensionsによると、厳密には、

Depends:
  R (>= 2.10)

も指定しないといけないらしい。

6.1.2 \uXXXXのエスケープを使う

これがR Packagesが推奨している方法3stringi::stri_escape_unicode()を使うとエスケープされた文字列を知ることができる。たとえば、「あ」という文字列を使いたいなら、

cat(stringi::stri_escape_unicode("あ"))
## \u3042

ということで、"あ"の代わりに"\u3042"と書けばいい。UTF-8になっているので、デフォルトの文字コードと合わせたいならenc2native()する必要がある点に注意。

6.1.3 データに含める

UTF-8にした文字列をデータとしてパッケージに含める、という手もある。 ただし、これは最近、CRAN checkの結果に

Result: NOTE
     Note: found 52458 marked UTF-8 strings 

という表示が出るのが気になる。いずれ使えなくなる?

6.1.4 メッセージの翻訳の仕組みを使う

あまり使われているのを見かけないが、Rにはメッセージの翻訳の仕組みがある4。 日本語を含めたい理由がメッセージを表示させるためであれば、これを使う手もある。具体的には、以下の流れになる。

  1. tools::update_pkg_po(".")を実行する。ソースコード中のstop()warning()message()のメッセージを読み取ってpo/R-パッケージ名.potというメッセージ翻訳のテンプレートができる。
  2. テンプレートファイルをコピーしてpo/R-ja.po(またはpo/ja.po)というファイルをつくる。
  3. inst下にインストールする5 。Windowsの場合あらかじめgettextのインストールが必要6
mkdir -p inst/po/ja/LC_MESSAGES
msgfmt -c --statistics -o inst/po/ja/LC_MESSAGES/R-パッケージ名.mo po/R-ja.po

なお、翻訳の仕組みを使う場合、stop()等にはdomain = "R-パッケージ名"を指定しておいた方が無難。

6.2 dynamic registeration of S3 method

6.3 Rcpp

6.4 C

「Advanced R」のCの関数についての章はまだ第二版にはないのでひとまず第一版を参照。

6.4.1 ヘッダファイル

Cの関数を使えるようにするには以下のヘッダをincludeする。

#include <R.h>
#include <Rinternals.h>

6.4.1.1 R_NO_REMAP

R Internals(Hadley版の方)によると、

we recommend that you use R_NO_REMAP so all API functions have the prefix R_ or Rf_

とあるが、これをdefineするとallocVector()asReal()のような関数も動かなくなってしまうので注意。

6.4.1.2 USE_RINTERNALS

R Internals(公式)によれば、

The only way to obtain direct access to the internals of the SEXPRECs is to define 'USE_RINTERNALS' before including header file Rinternals.h,

とある。rlangとかdplyrでも使ってないので、たぶん必要になることはなさそう。

6.4.2 関数の登録

Rcppと違って、Cの関数は自分で登録しないといけない。具体的には、init.cみたいなやつを作って以下を書く。 例えばSEXP foo(SEXP, SEXP);というCの関数を別ファイルで定義しているとすると、以下のようになる。

なお、R_registerRoutines()あたりについては、R 3.4以降のことなのでAdvanced R第1版にも載っていない。 Registering Routines with Rcppあたりを参考のこと(うまく説明できない)。

#include "./foo.h"

// TODO: これはincludeしなくてもコンパイルできたので要らないのかも
#include <R_ext/Rdynload.h>

// 同名・同じシグネチャの関数をexternで定義する
extern SEXP foo(SEXP, SEXP);

// R_registerRoutines()で関数を登録するためのエントリ
static const R_CallMethodDef CallEntries[] = {
  {"foo", (DL_FUNC) &foo, 2},  // 最後の数字は引数の数
  {NULL, NULL, 0}              // 最後にこのNULLのエントリを入れるのがお作法っぽい
};

// 関数名はR_init_<DLL名>にする。<DLL名>は@useDynLibに指定するもの
void R_init_foo(DllInfo *dll) {
  R_registerRoutines(dll, NULL, CallEntries, NULL, NULL);
  R_useDynamicSymbols(dll, FALSE);
}

あと、Rの側では、以下のように@useDynLibを指定しておく必要がある。

#' @useDynLib foo, .registration = TRUE

6.5 ALTREP

6.5.1 C++でALTREPを使う

<R_ext/Altrep.h>はR 3.5.xの時点ではC++に対応していない。r-develでの指摘7 を受けて修正が入った8 のでR 3.6.0以降で使うには問題ない。

Romain François氏によれば、以下のようにすればいいらしい9

#if R_VERSION < R_Version(3, 6, 0)
  #define class klass
  extern "C" {
    #include <R_ext/Altrep.h>
  }
  #undef class
#else
  #include <R_ext/Altrep.h>
#endif

より長いバージョンは以下のレポジトリを参照。

6.6 Apache Arrow