74

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 ?

David Arenburg
  • 91,361
  • 17
  • 137
  • 196
Pierre
  • 34,472
  • 31
  • 113
  • 192
  • 2
    I really wonder why they came up with such an unusual implementation. – anilbey Feb 10 '18 at 13:08
  • 6
    Objects or primitives? S3, S4 or R6? Using environments or otherwise? R 1.x, 2.x or 3.x? The answers here span 2010-15 and they disagree with each other. This question is an unholy mess and needs cleaning up. Also, it's useful when saying 'Yes/No' to cite releases or dates: e.g. 'as of R 3.0 / 2013'. To future-proof the answer. – smci May 01 '18 at 10:01

9 Answers9

55

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.

krlmlr
  • 25,056
  • 14
  • 120
  • 217
doug
  • 69,080
  • 24
  • 165
  • 199
52

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
Ari B. Friedman
  • 71,271
  • 35
  • 175
  • 235
  • 8
    Very helpful to know! I'd also add that this appears to apply ONLY to data.frames. Matrices/Arrays are always pass-by-value, as I just learned after a few hours of scratching at Rprof output. – Andrew Christianson Jun 04 '14 at 17:58
  • 2
    re-running this on my laptop now: all times are the same now (and half those of five years ago) – user189035 Mar 13 '17 at 13:08
  • The tracemem part need to be explained a little – cloudscomputes Jun 21 '18 at 09:54
  • @AndrewChristianson IIUC, this is the current behavior, also for `matrix`, `array` and `tibble`. Has the behavior changed since you posted your comment, or am I wrong? – Oren Milman Oct 21 '19 at 16:23
  • @OrenMilman oh gosh, probably? that comment is from quite a few years ago, and was probably made in reference to R 2.15 / 2.14, which I was using at the time. – Andrew Christianson Oct 21 '19 at 16:39
30

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"
Rappster
  • 12,762
  • 7
  • 71
  • 120
26

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

smci
  • 32,567
  • 20
  • 113
  • 146
teucer
  • 6,060
  • 2
  • 26
  • 36
6

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

Kyle Brandt
  • 26,938
  • 37
  • 124
  • 165
4

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

3

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

Jules Sam. Randolph
  • 3,610
  • 2
  • 31
  • 50
  • 1
    R6 is a user contributed package. Reference classes is something that comes with R (or rather its methods package). R6 is similar to reference classes, as the documentation puts it: "R6 classes are similar to R’s standard reference classes". – Helix123 Dec 25 '17 at 22:08
2

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

codeola
  • 838
  • 6
  • 14
2

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.

Hugo Raguet
  • 418
  • 3
  • 11