您可以在R中通过引用吗?


68

您可以通过引用加上“ 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"

可能吗 ?


2
我真的很奇怪为什么他们提出了这样一个不寻常的实现。
anilbey

4
对象还是基元?S3,S4或R6?使用环境还是其他方式?R 1.x,2.x或3.x?答案跨度为2010-15年,彼此之间意见分歧。这个问题是一团糟,需要清理。另外,在说出“是/否”来引用发行版或日期时也很有用:例如“自R 3.0 / 2013起”。为了面向未来的答案。
smci '18年

Answers:


53

没有

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


5
另请参见mutatrproto软件包。
hadley'4

mutatr似乎不受支持且没有记录。
krlmlr 2012年

@doug可以使用.CallC ++的包装程序通过引用传递吗?
nopeva '16

12
我发现这个“不”。相反,井,粗体,因为许多包似乎允许通过参考通道, 以及 Rcpp接口与C / C ++。
雨果·拉格

44

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

6
知道非常有帮助!我还要补充一点,这似乎仅适用于data.frames。矩阵/数组始终是按值传递的,正如我在Rprof输出上花了几个小时才得知的那样。
Andrew Christianson

1
现在在我的笔记本电脑上重新运行它:现在所有时间都是相同的(和五年前的一半一样)
user189035

关于tracemem的部分需要解释一下
-cloudcomputes

@AndrewChristianson IIUC,这是当前的行为,也为matrixarraytibble。自您发表评论以来,行为是否有所改变,还是我错了?
奥伦·米尔曼

@OrenMilman哦,天哪,也许吗?该评论来自几年前,很可能是参考我当时使用的R 2.15 / 2.14提出的。
Andrew Christianson

25

正如一些人之前指出的,这可以通过使用class对象来完成environment。存在使用environments建立的正式方法。它称为参考类,使您的工作变得非常简单。检查?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"



4

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


3

正如其他人所述,S4类是不可能的。但是,R现在为R6库(称为参考类)提供了可能性。参阅官方文件


1
R6是用户提供的软件包。引用类是R(或其方法包)附带的东西。R6与参考类相似,如文档所述:“ R6类与R的标准参考类相似”。
Helix123 '17

2

除了此处实际通过引用传递对象(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)函数内的任何地方,你会从外面得到他们尚未未修改的值。另外,此实现只将简单的变量名称作为参数来处理,因此诸如的索引参数f(x[1], ...)将不起作用(尽管您可以通过更多的表达式操作来实现,以回避受限的assign)。


By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.