2012-07-02 12 views
13

me hizo un primer intento de una función de RCPP través inline y se resolvió mi problema de velocidad (gracias a Dirk!): R: Replacing negative values by zeropase por RCPP vs. referencia por el valor

La versión inicial era la siguiente:

library(inline) 
cpp_if_src <- ' 
    Rcpp::NumericVector xa(a); 
    int n_xa = xa.size(); 
    for(int i=0; i < n_xa; i++) { 
    if(xa[i]<0) xa[i] = 0; 
    } 
    return xa; 
' 
cpp_if <- cxxfunction(signature(a="numeric"), cpp_if_src, plugin="Rcpp") 

Pero cuando se llama cpp_if(p), sobrescribió p con la salida, que no era la prevista. Así que supuse que pasaba por referencia.

Así lo arreglé con la versión siguiente:

library(inline) 
cpp_if_src <- ' 
    Rcpp::NumericVector xa(a); 
    int n_xa = xa.size(); 
    Rcpp::NumericVector xr(a); 
    for(int i=0; i < n_xa; i++) { 
    if(xr[i]<0) xr[i] = 0; 
    } 
    return xr; 
' 
cpp_if <- cxxfunction(signature(a="numeric"), cpp_if_src, plugin="Rcpp") 

que parecía funcionar. Sin embargo, ahora la versión original no sobrescribe su entrada más cuando me re-cargarlo en R (es decir, el mismo código ya no sobrescribe su entrada):

> cpp_if_src <- ' 
+ Rcpp::NumericVector xa(a); 
+ int n_xa = xa.size(); 
+ for(int i=0; i < n_xa; i++) { 
+  if(xa[i]<0) xa[i] = 0; 
+ } 
+ return xa; 
+ ' 
> cpp_if <- cxxfunction(signature(a="numeric"), cpp_if_src, plugin="Rcpp") 
> 
> p 
[1] -5 -4 -3 -2 -1 0 1 2 3 4 5 
> cpp_if(p) 
[1] 0 0 0 0 0 0 1 2 3 4 5 
> p 
[1] -5 -4 -3 -2 -1 0 1 2 3 4 5 

que no soy el único que ha tratado de reproducir este comportamiento y se han encontrado resultados contradictorios:

http://chat.stackoverflow.com/transcript/message/4357344#4357344

¿Qué está pasando aquí?

+0

¿Puedes reformular tu pregunta? ¿Usted, o no, quiere sobrescribir? Me parece que la versión 2 logra lo que se propuso hacer ... Además, hay una lista de correo dedicada dedicada a Rcpp donde es probable que obtenga respuestas decentes. –

+0

Trató de editar para mayor claridad. No quiero que sobrescriba. Si esto no es obvio, entonces supongo que debería publicar en la lista de correo, pero no quería molestar a la gente de lo contrario. –

Respuesta

18

La clave es 'modelo proxy' - su xa es realmente la misma ubicación de memoria que su objeto original por lo que termina cambiando su original.

Si no quiere eso, debe hacer una cosa: copiar (en profundidad) utilizando el método clone(), o tal vez crear explícitamente un nuevo objeto en el que se escriba el objeto alterado. El método dos no no hacer eso, simplemente usa dos variables con nombres diferentes que son tanto "punteros" (en el sentido del modelo proxy) como la variable original.

Una complicación adicional, sin embargo, es en implícita fundido y copiar cuando se pasa un vector int (de R) a un tipo NumericVector: que crea una copia, y luego el original ya no se altera.

Aquí es un ejemplo más explícito, similar a la que yo uso en los tutoriales o talleres:

library(inline) 
f1 <- cxxfunction(signature(a="numeric"), plugin="Rcpp", body=' 
    Rcpp::NumericVector xa(a); 
    int n = xa.size(); 
    for(int i=0; i < n; i++) { 
    if(xa[i]<0) xa[i] = 0; 
    } 
    return xa; 
') 

f2 <- cxxfunction(signature(a="numeric"), plugin="Rcpp", body=' 
    Rcpp::NumericVector xa(a); 
    int n = xa.size(); 
    Rcpp::NumericVector xr(a);   // still points to a 
    for(int i=0; i < n; i++) { 
    if(xr[i]<0) xr[i] = 0; 
    } 
    return xr; 
') 

p <- seq(-2,2) 
print(class(p)) 
print(cbind(f1(p), p)) 
print(cbind(f2(p), p)) 
p <- as.numeric(seq(-2,2)) 
print(class(p)) 
print(cbind(f1(p), p)) 
print(cbind(f2(p), p)) 

y esto es lo que veo:

[email protected]:~/svn/rcpp/pkg$ r /tmp/ari.r 
Loading required package: methods 
[1] "integer" 
     p 
[1,] 0 -2 
[2,] 0 -1 
[3,] 0 0 
[4,] 1 1 
[5,] 2 2 
     p 
[1,] 0 -2 
[2,] 0 -1 
[3,] 0 0 
[4,] 1 1 
[5,] 2 2 
[1] "numeric" 
     p 
[1,] 0 0 
[2,] 0 0 
[3,] 0 0 
[4,] 1 1 
[5,] 2 2 
     p 
[1,] 0 0 
[2,] 0 0 
[3,] 0 0 
[4,] 1 1 
[5,] 2 2 
[email protected]:~/svn/rcpp/pkg$ 

Por lo que realmente importa si se pasa int-to-float o float-to-float.

+1

Gracias Dirk. Eso me dice cómo debería haber escrito la función (y la archivaré para usarla en el futuro). Y creo que el reparto implícito y la copia probablemente explican la aparente inconsistencia. –

+1

Es un rascador de cabeza la primera vez que lo encuentra, pero sí, tiene sentido. –

+0

Muy interesante. Un inconveniente menor para la claridad: ¿no debería p redefinirse después de cada llamada a la función, especialmente la segunda llamada a f1? De lo contrario, es la p alterada la que se está alimentando a f2 ... ¿verdad? –

Cuestiones relacionadas