你能在 R 中通过引用传递吗?

你能用“ 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"

有可能吗?

62149 次浏览

没有。

赋值语句中的对象是不可变的。 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

(但是: 上面的陈述对于 R 原始人是正确的,例如,向量,矩阵) ,对于 功能也是正确的; 我不能确定对于 所有 R 对象是否是正确的——只是它们中的大多数,以及绝大多数最常用的

如果您不喜欢这种行为,您可以选择在 R 软件包的帮助下避免这种行为。例如,有一个名为 的 R 包,它允许您模拟引用传递行为; 在 CRAN上可以使用 R.oo。

对于 environment,可以通过引用传递。要使用它们,基本上无论何时创建对象,都需要创建一个环境槽。但是我认为这很麻烦。看看这个 通过引用传递 S4。 和 < a href = “ http://www.stat.berkeley.edu/~ paciorek/computingTips/Pointers _ Pass _ reference _.html”rel = “ noReferrer”> 指针,在 R 中通过引用传递

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

现在 R 确实有一个库,允许您使用引用进行 OOP。请参见 参考课程,它是方法包的一部分。

正如前面已经指出的,这可以通过使用类 environment的对象来实现。存在一种基于 environment使用的正式方法。它被称为 参考课程,让事情对你来说真的很容易。检查 ?setRefClass以获得主要的入口帮助页面。它还描述了如何在引用类中使用形式化方法。

例子

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"

注意,如果您希望仅仅使用引用传递来避免复制未修改的对象所带来的性能影响(这在其他具有常量引用的语言中很常见) ,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

除了实际上通过引用 通过你的对象(environment对象和引用类)的其他答案之外,如果你纯粹对通过引用调用语法的方便性感兴趣(也就是说你不介意你的数据复制在内部) ,你可以通过将最终值返回给外部变量来模拟它,同时返回:

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

然后我们可以声明“引用调用”参数:

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来实现它)。

正如其他人所说,这是不可能的中四类。但是 R 现在提供了使用 R6库的可能性,称为 参考文献类。见 正式文件

在其他建议的基础上,您还可以编写 C/C + + 函数,通过引用获取它们的参数并使用 就位,并使用 Rcpp(等等)直接在 R 中调用它们。 特别是 这个答案