web-dev-qa-db-fra.com

Pouvez-vous passer par référence dans R?

Pouvez-vous passer par référence avec "R"? par exemple, dans le code suivant:

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

la sortie est

> 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"

mais j'aimerais que ce soit

> 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"

c'est possible ?

60
Pierre

Non .

Les objets des instructions d'affectation sont immuables. R copiera l'objet et non juste la référence.

> 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

( provisoire: l'énoncé ci-dessus est vrai pour R primitives, par exemple, vecteurs, matrices), ainsi que pour fonctions ; Je ne peux pas dire avec certitude si c'est vrai pour les objets tous R - juste la plupart d'entre eux, ainsi que la grande majorité de ceux les plus souvent utilisés.)

Si vous n'aimez pas ce comportement, vous pouvez le désactiver à l'aide d'un package R. Par exemple, il existe un package R appelé R.oo qui vous permet d'imiter le comportement de passage par référence; R.oo est disponible sur CRAN .

53
doug

Notez que si vous espérez utiliser pass-by-reference simplement pour éviter les implications en termes de performances de la copie d'un objet qui n'est pas modifié (comme cela est courant dans d'autres langues avec des références constantes), R le fait automatiquement:

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
38
Ari B. Friedman

Comme plusieurs l'ont déjà souligné, cela peut être fait en utilisant des objets de classe environment. Il existe une approche formelle basée sur l'utilisation de environments. Cela s'appelle classes de référence et rend les choses vraiment faciles pour vous. Vérifier ?setRefClass pour la page principale d'aide à la saisie. Il décrit également comment utiliser des méthodes formelles avec des classes de référence.

Exemple

setRefClass("MyClass",
    fields=list(
        name="character"
    )
)

instance1 <- new("MyClass",name="Hello1")
instance2 <- new("MyClass",name="Hello2")

array = c(instance1,instance2)

instance1$name <- "World!"

Production

> 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"
19
Rappster

Le passage par référence est possible pour environments. Pour les utiliser, chaque fois que vous créez un objet, vous devez également créer un emplacement d'environnement. Mais je pense que c'est lourd. Jetez un oeil à Passez par référence pour S4. et Pointeurs et passant par référence dans R

16
teucer

R a maintenant une bibliothèque qui vous permet de faire OOP en utilisant des références. Voir ReferenceClasses qui fait partie du paquet de méthodes.

5
Kyle Brandt

En fait, le package R.oo émule le comportement passe-par-référence en utilisant des environnements.

4
user350780

Comme d'autres l'ont dit, ce n'est pas possible pour les classes S4. Mais R offre désormais la possibilité avec la bibliothèque R6 , appelée classes de référence . Voir documentation officielle

3
Jules Randolph

En plus des autres réponses ici qui en fait pass votre objet par référence (environment objets et classes de référence), si vous êtes purement intéressé par l'appel par référence pour des raisons de syntaxe ( c'est-à-dire que cela ne vous dérange pas que vos données soient copiées à l'intérieur), vous pouvez émuler cela en attribuant la valeur finale à la variable extérieure tout en retournant:

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

Ensuite, nous pouvons déclarer des arguments "appel par référence":

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

Notez que si vous accédez aux variables "par référence" par leurs noms extérieurs (x1, x3) N'importe où à l'intérieur de la fonction, vous obtiendrez de l'extérieur leurs valeurs encore non modifiées. En outre, cette implémentation ne gère que les noms de variables simples en tant qu'arguments, donc les arguments indexés tels que f(x[1], ...) ne fonctionneront pas (bien que vous puissiez probablement implémenter cela avec une manipulation d'expression un peu plus compliquée pour contourner le limité assign).

2
codeola

En plus des autres suggestions, vous pouvez également écrire des fonctions C/C++ en prenant leurs arguments par référence et en travaillant sur place , et les appeler directement dans R grâce à Rcpp (entre autres). Voir en particulier cette réponse .

1
Hugo Raguet