2012-01-09 23 views
10

estoy escribiendo algo de código en C para llamar dinámicamente a partir de R.un vector dinámico de C a R

Este código genera un camino de forma aleatoria de Poisson proceso hasta un tiempo T. Así deseada en cada llamada a mi función C, la longitud del vector devuelto será diferente dependiendo de los números aleatorios generados.

¿Qué estructura de datos de R tendré que crear? a LISTSXP? ¿otro?

¿Cómo puedo crearlo y cómo puedo agregarlo? Y especialmente ¿cómo puedo devolverlo a R?

Gracias por la ayuda.

+0

Es posible que desee comprobar los ejemplos de la "escritura R Extensiones" http Manual: //cran.r-project.or g/doc/manuals/R-exts.pdf (capítulo 5). O necesitas un vector C de dobles 'double *' o un vector R 'REALSXP'. –

Respuesta

5

Depende de usted lo que quiere utilizar como estructura temporal, porque al final tendrá que asignar un vector para el resultado de todos modos. Entonces, lo que sea que vayas a usar no es lo que devolverás. Hay varias posibilidades:

  1. uso Calloc + Realloc + Free que le permite ampliar la memoria temporal, según sea necesario. Una vez que tiene el conjunto completo, asigna el vector de resultado y lo devuelve.
  2. si puede sobreestimar el tamaño fácilmente, puede sobreasignar el vector de resultado y usar SETLENGTH antes de devolverlo. Sin embargo, hay problemas con esto, porque el resultado seguirá estando sobreasignado hasta que se duplique más adelante.
  3. Puede usar lo que insinuó que es una lista encadenada de bloques vectoriales, es decir, asignar y proteger una paleta de la que añada vectores a la cola cuando los necesite. Al final, asigna el vector de retorno y copia el contenido de los bloques que asignó. Esto es más intrincado que los dos anteriores.

Cada uno de ellos tiene inconvenientes y beneficios, por lo que realmente depende de su aplicación para elegir el que mejor se adapte a usted.

Editar: añadió un ejemplo del uso del enfoque de emparejamientos. Aun así, recomendaría el enfoque Realloc ya que es mucho más fácil, pero no obstante:

#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; 
} 
+0

Gracias por su respuesta. Luego crearé una estructura de lista enlazada para memorizar mis datos temporales, antes de tener toda la longitud y copiarla en un vector. Pensé que tal estructura existía en R Internals. –

+0

Una pairlist (LISTSXP) es una lista enlazada, por lo que no es necesario crear esa estructura; la única razón para no usar 'Realloc' (que de otra forma es la más fácil y más conveniente) es precisamente mantener todos los objetos como objetos R - como dije, asignaría y protegería una pairlist como la raíz de tu cadena y anexaría los vectores recién asignados a medida que avanzas (eso soluciona la pesadilla de protección). –

+0

Muchas gracias ... Por favor, puede dar un código de samle C, construir un LISTXP de dobles y agregarle algunos valores, luego recuperar estos valores de nuevo ... No pude encontrar ninguna referencia de API a este tipo de tipo interno de R .. Gracias de nuevo. –

4

Si usted está abierto a cambiar de C a C++, se obtiene la capa de RCPP añadido de forma gratuita. He aquí un ejemplo de la página del paquete RcppExample (todavía bastante INCOMPLE):

#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 
} 

Como se ve, hay una asignación de memoria explícita, liberando, PROTECT/UNPROTECT etc, y se obtiene un primer objeto de la lista de clase I espalda.

Hay muchos más ejemplos, incluido in other SO questions such as this one.

Editar: Y que en realidad no dice lo que usted caminos haría, sino como una simple ilustración, aquí está el código C++ utilizando las adiciones RCPP cumsum() y rpois() que se comportan igual que lo hacen en I:

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 

Y como prueba, de vuelta en R, si ponemos la semilla, podemos generar exactamente los mismos números:

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> 
+0

Gracias por su respuesta. Realmente parece que Rcpp merece una mirada cercana. Intentaré sumergirme en él. –