你可以通过引用“R”来传递吗? 例如,在以下代码中:
setClass("MyClass",
representation(
name="character"
))
instance1 <-new("MyClass",name="Hello1")
instance2 <-new("MyClass",name="Hello2")
array = c(instance1,instance2)
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"
有可能吗?
没有。
赋值语句中的对象是不可变的。 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.oo 的 R 包,它允许您模仿传递引用行为; R.oo 可在 CRAN 上使用。
请注意,如果您希望使用引用传递只是为了避免复制未修改的对象对性能造成的影响(这在其他具有常量引用的语言中很常见),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
类的对象来完成。存在一种基于 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"
包通过使用环境来模拟传递引用行为。
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
请注意,如果您在函数内的任何位置通过外部名称(
x1
、x3
)访问“按引用”变量,您将从外部获取它们尚未修改的值。此外,此实现仅将简单的变量名作为参数处理,因此诸如
f(x[1], ...)
之类的索引参数将不起作用(尽管您可能可以通过更多涉及的表达式操作来实现它,以避开有限的
assign
)。除了其他建议之外,您还可以编写 C/C++ 函数,通过引用获取参数并“就地”工作,并通过