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 ?
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 .
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
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 environment
s. 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.
setRefClass("MyClass",
fields=list(
name="character"
)
)
instance1 <- new("MyClass",name="Hello1")
instance2 <- new("MyClass",name="Hello2")
array = c(instance1,instance2)
instance1$name <- "World!"
> 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"
Le passage par référence est possible pour environment
s. 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
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.
En fait, le package R.oo émule le comportement passe-par-référence en utilisant des environnements.
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
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
).
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 .