你可以在R中传递参考吗?

时间:2010-04-08 20:23:38

标签: r oop parameter-passing pass-by-reference

你可以通过引用传递“R”吗? 例如,在以下代码中:

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

输出

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

但我希望它是

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

有可能吗?

9 个答案:

答案 0 :(得分:54)

赋值语句中的对象是不可变的。 R将复制不是 引用的对象。

> 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 :上述陈述适用于R 原语,例如矢量,矩阵),也适用于函数;我不能肯定地确定所有 R对象是否属实 - 只是其中大部分,以及绝大多数最常用的对象。)

如果您不喜欢这种行为,可以在R套餐的帮助下选择退出。例如,有一个名为 R.oo 的R包,它允许您模仿传递引用行为; R.oo可在CRAN上找到。

答案 1 :(得分:38)

请注意,如果您希望仅使用pass-by-reference来避免复制未修改对象的性能影响(在常量引用的其他语言中很常见),R会自动执行此操作:

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

答案 2 :(得分:18)

如前所述,可以通过使用类environment的对象来完成。在environment s的使用基础上存在一种正式的方法。它被称为参考类,让事情变得非常简单。检查?setRefClass以获取主条目帮助页面。它还描述了如何使用带有Reference Classes的形式化方法。

实施例

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"

答案 3 :(得分:16)

environment可以传递参考号。要使用它们,基本上每当您创建对象时,您都需要创建一个环境槽。但我认为这很麻烦。看一下 Pass by reference for S4.Pointers and passing by reference in R

答案 4 :(得分:5)

R现在有一个库,允许您使用引用进行OOP。请参阅ReferenceClasses,它是方法包的一部分。

答案 5 :(得分:4)

实际上 R.oo 包通过使用环境来模拟传递引用行为。

答案 6 :(得分:3)

正如其他人所说,S4课程不可能。但是R现在提供了 R6 库的可能性,称为引用类。见official documentation

答案 7 :(得分:2)

除了这里的其他答案,实际上通过引用传递你的对象(environment对象和引用类),如果你纯粹对语法的call-by-reference感兴趣方便(即您不介意将数据复制到内部),您可以通过在返回时将最终值分配回外部变量来模拟它:

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

然后我们可以声明“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

请注意,如果您通过函数内部的任何位置(x1x3)访问“by-reference”变量,您将从外部获取尚未修改的值。此外,此实现仅处理简单的变量名称作为参数,因此诸如f(x[1], ...)之类的索引参数将不起作用(尽管您可能通过更多涉及的表达式操作来实现该操作以回避有限的assign)。

答案 8 :(得分:1)

除了其他建议之外,您还可以编写C / C ++函数,通过引用获取参数并使用就地,并通过Rcpp直接在R中调用它们(除其他外)。 特别参见this answer