Puoi passare per riferimento in R?

Puoi passare per riferimento con “R”? ad esempio, nel seguente codice:

setClass("MyClass", representation( name="character" )) instance1 <-new("MyClass",name="Hello1") instance2 <-new("MyClass",name="Hello2") array = c(instance1,instance2) instance1 array instance1@name="World!" instance1 array 

l’output è

 > instance1 An object of class “MyClass” Slot "name": [1] "World!" > array [[1]] An object of class “MyClass” Slot "name": [1] "Hello1" [[2]] An object of class “MyClass” Slot "name": [1] "Hello2" 

ma vorrei che lo fosse

 > instance1 An object of class “MyClass” Slot "name": [1] "World!" > array [[1]] An object of class “MyClass” Slot "name": [1] "World!" [[2]] An object of class “MyClass” Slot "name": [1] "Hello2" 

È ansible ?

No

Gli oggetti nelle dichiarazioni di assegnazione sono immutabili. R copierà l’object non solo il riferimento.

 > v = matrix(1:12, nrow=4) > v [,1] [,2] [,3] [1,] 1 5 9 [2,] 2 6 10 [3,] 3 7 11 [4,] 4 8 12 > v1 = v > v1[,1] # fetch the first column [1] 1 2 3 4 

(a condizione : la dichiarazione sopra è vera per i primitivi R, ad esempio, vettori, matrici), e anche per le funzioni ; Non posso dire con certezza se è vero per tutti gli oggetti R – solo per la maggior parte di essi, così come per la stragrande maggioranza di quelli più usati.)

Se non ti piace questo comportamento, puoi rinunciarvi con l’aiuto di un pacchetto R. Ad esempio, esiste un pacchetto R chiamato R.oo che consente di simulare il comportamento del pass-per-riferimento; R.oo è disponibile su CRAN .

Si noti che se si spera di utilizzare il pass-per-riferimento semplicemente per evitare le implicazioni di prestazioni della copia di un object che non è modificato (come è comune in altre lingue con riferimenti costanti), R lo fa automaticamente:

 n < - 10^7 bigdf <- data.frame( x=runif(n), y=rnorm(n), z=rt(n,5) ) myfunc <- function(dat) invisible(with( dat, x^2+mean(y)+sqrt(exp(z)) )) myfunc2 <- function(dat) { x <- with( dat, x^2+mean(y)+sqrt(exp(z)) ) invisible(x) } myfunc3 <- function(dat) { dat[1,1] <- 0 invisible( with( dat, x^2+mean(y)+sqrt(exp(z)) ) ) } tracemem(bigdf) > myfunc(bigdf) > # nothing copied > myfunc2(bigdf) > # nothing copied! > myfunc3(bigdf) tracemem[0x6e430228 -> 0x6b75fca0]: myfunc3 tracemem[0x6b75fca0 -> 0x6e4306f0]: [< -.data.frame [<- myfunc3 tracemem[0x6e4306f0 -> 0x6e4304f8]: [< -.data.frame [<- myfunc3 > > library(microbenchmark) > microbenchmark(myfunc(bigdf), myfunc2(bigdf), myfunc3(bigdf), times=5) Unit: milliseconds expr min lq median uq max 1 myfunc2(bigdf) 617.8176 641.7673 644.3764 683.6099 698.1078 2 myfunc3(bigdf) 1052.1128 1134.0822 1196.2832 1202.5492 1206.5925 3 myfunc(bigdf) 598.9407 622.9457 627.9598 642.2727 654.8786 

Come molti hanno sottolineato prima, questo può essere fatto usando oggetti di class environment . Esiste un approccio formale basato sull’uso environment . Si chiama Reference Classes e rende le cose davvero facili per te. Controllare ?setRefClass per la pagina della ?setRefClass alla voce principale. Descrive anche come usare i metodi formali con le classi di riferimento.

Esempio

 setRefClass("MyClass", fields=list( name="character" ) ) instance1 < - new("MyClass",name="Hello1") instance2 <- new("MyClass",name="Hello2") array = c(instance1,instance2) instance1$name <- "World!" 

Produzione

 > instance1 Reference class object of class "MyClass" Field "name": [1] "World!" > array [[1]] Reference class object of class "MyClass" Field "name": [1] "World!" [[2]] Reference class object of class "MyClass" Field "name": [1] "Hello2" 

Pass-by-reference è ansible per l’ environment s. Per usarli, in pratica ogni volta che crei un object, dovresti creare anche uno spazio per l’ambiente. Ma penso che sia ingombrante. Dai un’occhiata a Pass per riferimento per S4. e puntatori e passando per riferimento in R

R ha ora una libreria che ti permette di fare OOP usando i riferimenti. Vedi ReferenceClasses che fa parte del pacchetto dei metodi.

In realtà il pacchetto R.oo emula il comportamento del pass-by-reference usando gli ambienti.

Come altri hanno affermato, non è ansible per le classi S4. Ma R ora offre la possibilità con la libreria R6 , chiamata classi di riferimento . Vedi la documentazione ufficiale

Oltre alle altre risposte qui che in realtà passano il tuo object per riferimento (oggetti di environment e classi di riferimento), se sei puramente interessato alla call-by-reference per comodità sintattica (cioè non ti interessa copiare i tuoi dati all’interno), è ansible emularlo assegnando il valore finale alla variabile esterna durante la restituzione:

 byRef < - function(..., envir=parent.frame(), inherits=TRUE) { cl <- match.call(expand.dots = TRUE) cl[c(1, match(c("envir", "inherits"), names(cl), 0L))] <- NULL for (x in as.list(cl)) { s <- substitute(x) sx <- do.call(substitute, list(s), envir=envir) dx <- deparse(sx) expr <- substitute(assign(dx, s, envir=parent.frame(), inherits=inherits)) do.call(on.exit, list(expr, add=TRUE), envir=envir) } } 

Quindi possiamo dichiarare argomenti "call-by-reference":

 f < - function(z1, z2, z3) { byRef(z1, z3) z1 <- z1 + 1 z2 <- z2 + 2 z3 <- z3 + 3 c(z1, z2, z3) } x1 <- 10 x2 <- 20 x3 <- 30 # Values inside: print(f(x1, x2, x3)) # [1] 11 22 33 # Values outside: print(c(x1, x2, x3)) # [1] 11 20 33 

Nota che se accedi alle variabili "per riferimento" con i loro nomi esterni ( x1 , x3 ) ovunque all'interno della funzione, otterrai i loro valori non modificati dall'esterno. Inoltre, questa implementazione gestisce solo nomi di variabili semplici come argomenti, quindi argomenti indicizzati come f(x[1], ...) non funzioneranno (anche se probabilmente potresti implementarlo con un po 'più di manipolazione dell'espressione implicita per eludere l' assign limitata ).

Oltre agli altri suggerimenti, puoi anche scrivere funzioni C / C ++ prendendo i loro argomenti per riferimento e lavorando sul posto , e chiamali direttamente in R grazie a Rcpp (tra gli altri). Vedi in particolare questa risposta .