Can you pass-by-reference in R?

ROopParameter PassingPass by-Reference

R Problem Overview


Can you pass by reference with "R" ? for example, in the following code:

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

the output is

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

but I wish it was

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

is it possible ?

R Solutions


Solution 1 - R

No.

Objects in assignment statements are immutable. R will copy the object not just the reference.

> 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

(proviso: the statement above is true for R primitives, e.g., vectors, matrices), and also for functions; I cannot say for certain whether it's true for all R objects--just most of them, as well as the vast majority of the ones most often used.)

If you don't like this behavior you can opt out of it with the help from an R Package. E.g., there is an R Package called R.oo that allows you to mimic pass-by-reference behavior; R.oo is available on CRAN.

Solution 2 - R

Note that if you hope to use pass-by-reference simply to avoid the performance implications of copying an object that isn't modified (as is common in other languages with constant references), R does this automatically:

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

Solution 3 - R

As several have pointed out before, this can be done via using objects of class environment. There exists a formal approach building upon the use of environments. It's called Reference Classes and makes things really easy for you. Check ?setRefClass for the main entry help page. It also describes how to use formal methods with Reference Classes.

Example

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

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

array = c(instance1,instance2)

instance1$name <- "World!"

Output

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

Solution 4 - R

Pass-by-reference is possible for environments. To use them, basically whenever you create an object you would need to create an environment slot as well. But I think that it is cumbersome. Have a look at Pass by reference for S4. and Pointers and passing by reference in R

Solution 5 - R

R does have a library now that allows you to do OOP using references. See ReferenceClasses which is part of the methods package.

Solution 6 - R

Actually the R.oo package emulates the pass-by-reference behaviour by using environments.

Solution 7 - R

As other have stated, it's not possible for S4 classes. But R now provides the possibility with R6 library, called reference classes. See official documentation

Solution 8 - R

In addition to the other answers here that actually pass your object by reference (environment objects and Reference Classes), if you're purely interested in call-by-reference for syntactic convenience (i.e. you don't mind your data copied inside), you could emulate that by assigning the final value back to the outside variable while returning:

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

Then we can declare "call-by-reference" arguments:

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

Note that if you access the "by-reference" variables by their outside names (x1, x3) anywhere inside the function, you'll get their yet-unmodified values from the outside. Also, this implementation only handles simple variable names as arguments, so indexed arguments such as f(x[1], ...) will not work (though you could probably implement that with a bit more involved expression manipulation to sidestep the limited assign).

Solution 9 - R

On top of the other suggestions, you can also write C/C++ functions taking their arguments by reference and working in-place, and call them directly in R thanks to Rcpp (among others). See in particular this answer.

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionPierreView Question on Stackoverflow
Solution 1 - RdougView Answer on Stackoverflow
Solution 2 - RAri B. FriedmanView Answer on Stackoverflow
Solution 3 - RRappsterView Answer on Stackoverflow
Solution 4 - RteucerView Answer on Stackoverflow
Solution 5 - RKyle BrandtView Answer on Stackoverflow
Solution 6 - Ruser350780View Answer on Stackoverflow
Solution 7 - RJules Sam. RandolphView Answer on Stackoverflow
Solution 8 - RcodeolaView Answer on Stackoverflow
Solution 9 - RHugo RaguetView Answer on Stackoverflow