11

Rから動的に呼び出されるコードをCで書いています。

このコードは、任意の時間 T までのランダムポアソンプロセスのパスを生成します。そのため、C 関数を呼び出すたびに、生成される乱数に応じて、返されるベクトルの長さが異なります。

どの R データ構造を作成する必要がありますか? LISTSXP ? もう一つ?

どうすればそれを作成でき、どのように追加できますか? 特にどうすればRに戻すことができますか?

手伝ってくれてありがとう。

4

2 に答える 2

6

一時的な構造として何を使用するかは、最終的に結果にベクトルを割り当てる必要があるため、実際にはあなた次第です。したがって、使用するものが何であれ、返されるものではありません。いくつかの可能性があります:

  1. Calloc+ Realloc+を使用Freeすると、必要に応じて一時メモリを拡張できます。完全なセットを取得したら、結果ベクトルを割り当てて返します。
  2. サイズを簡単に過大評価できる場合は、結果ベクトルを過剰に割り当てて、SETLENGTH返す前に使用できます。ただし、後で複製されるまで結果が過剰に割り当てられたままになるため、これには問題があります。
  3. ベクトルブロックの連鎖リストであると示唆したものを使用できます。つまり、必要に応じて末尾にベクトルを追加する1つのペアリストを割り当てて保護します。最後に、戻りベクトルを割り当て、割り当てたブロックのコンテンツをコピーします。これは、上記の両方よりも複雑です。

それぞれに欠点と利点があるため、アプリケーションによって最適なものを選択する必要があります。

編集:ペアリスト アプローチの使用例を追加しました。はるかに簡単なので、このアプローチをお勧めしますReallocが、それでも:

#define BLOCK_SIZE xxx /* some reasonable size for increments - could be adaptive, too */ 
SEXP block; /* last vector block */
SEXP root = PROTECT(list1(block = allocVector(REALSXP, BLOCK_SIZE)));
SEXP tail = root;
double *values = REAL(block);
int count = 0, total = 0;
do { /* your code to generate values - if you want to add one 
        first try to add it to the existing block, otherwise allocate new one */
    if (count == BLOCK_SIZE) { /* add a new block when needed */
        tail = SETCDR(tail, list1(block = allocVector(REALSXP, BLOCK_SIZE)));
        values = REAL(block);
        total += count;
        count = 0;
    }
    values[count++] = next_value;
} while (...);
total += count;
/* when done, we need to create the result vector */
{
    SEXP res = allocVector(REALSXP, total);
    double *res_values = REAL(res);
    while (root != R_NilValue) {
        int size = (CDR(root) == R_NilValue) ? count : BLOCK_SIZE;
        memcpy(res_values, REAL(CAR(root)), sizeof(double) * size);
        res_values += size;
        root = CDR(root);
    }
    UNPROTECT(1);
    return res;
 }
于 2012-01-10T00:46:57.293 に答える
5

CからC++への切り替えを受け入れる場合は、Rcppの追加レイヤーを無料で入手できます。(まだかなり不完全な)RcppExampleパッケージのページからの例を次に示します。

#include <RcppClassic.h>
#include <cmath>

RcppExport SEXP newRcppVectorExample(SEXP vector) {
BEGIN_RCPP

    Rcpp::NumericVector orig(vector);                // keep a copy 
    Rcpp::NumericVector vec(orig.size());            // create vector same size

    // we could query size via
    //   int n = vec.size();
    // and loop over the vector, but using the STL is so much nicer
    // so we use a STL transform() algorithm on each element
    std::transform(orig.begin(), orig.end(), vec.begin(), ::sqrt);

    return Rcpp::List::create(Rcpp::Named( "result" ) = vec,
                              Rcpp::Named( "original" ) = orig) ;

END_RCPP
}

ご覧のとおり、明示的なメモリ割り当てや解放PROTECT/UNPROTECTなどはなく、ファーストクラスのRリストオブジェクトが返されます。

このような他のSOの質問には、さらに多くの例が含まれています。

編集: そして、あなたはパスが何をするかを実際には言いませんでしたが、簡単な例として、Rcppの追加を使用し、Rの場合と同じように動作するC++コードを次に示しcumsum()ますrpois()

R> library(inline)
R> 
R> fun <- cxxfunction(signature(ns="integer", lambdas="numeric"),
+                    plugin="Rcpp",
+                    body='
+    int n = Rcpp::as<int>(ns);
+    double lambda = Rcpp::as<double>(lambdas);
+ 
+    Rcpp::RNGScope tmp;                  // make sure RNG behaves
+ 
+    Rcpp::NumericVector vec = cumsum( rpois( n, lambda ) );
+ 
+    return vec;
+ ')
R> set.seed(42)
R> fun(3, 0.3)
[1] 1 2 2
R> fun(4, 0.4)
[1] 1 1 1 2

そして証拠として、Rに戻って、シードを設定すると、まったく同じ数を生成できます。

R> set.seed(42)
R> cumsum(rpois(3, 0.3))
[1] 1 2 2
R> cumsum(rpois(4, 0.4))
[1] 1 1 1 2
R> 
于 2012-01-10T01:01:04.503 に答える