【问题标题】:Can you pass-by-reference in R?你可以在R中通过引用吗?
【发布时间】:2011-02-05 21:17:26
【问题描述】:

你能通过引用传递 "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"

有可能吗?

【问题讨论】:

  • 我真的很想知道他们为什么想出这样一个不寻常的实现。
  • 对象还是原语? S3、S4 还是 R6?使用环境还是其他方式? R 1.x、2.x 还是 3.x?这里的答案跨越 2010-15 年,他们彼此不同意。这个问题是一团糟,需要清理。此外,在说“是/否”来引用版本或日期时很有用:例如'截至 R 3.0 / 2013'。让答案永不过时。

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


【解决方案1】:

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

【讨论】:

  • 另见mutatrproto 包。
  • mutatr 似乎不受支持且未记录。
  • @doug 你可以使用 C++ 的 .Call 包装器通过引用传递吗?
  • 我找到了这个“不”。而是,bold,因为许多包似乎允许通过引用传递,as well asRcpp 与 C/C++ 接口。
【解决方案2】:

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

【讨论】:

  • 链接现在可以使用了。这些天应该将 S4 列在 S3 之前。
【解决方案3】:

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

【讨论】:

    【解决方案4】:

    R 现在确实有一个库,允许您使用引用进行 OOP。请参阅方法包中的ReferenceClasses

    【讨论】:

      【解决方案5】:

      正如一些人之前指出的,这可以通过使用类environment 的对象来完成。存在一种基于使用environments 的正式方法。它被称为Reference Classes,它让你的事情变得非常简单。查看?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"
      

      【讨论】:

        【解决方案6】:

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

        【讨论】:

        • 了解非常有帮助!我还要补充一点,这似乎仅适用于 data.frames。矩阵/数组始终是按值传递的,正如我在研究 Rprof 输出几个小时后才了解到的那样。
        • 现在在我的笔记本电脑上重新运行:现在所有时间都一样(和五年前的一半)
        • tracemem部分需要解释一下
        • @AndrewChristianson IIUC,这是当前的行为,也适用于 matrixarraytibble。自您发表评论以来,行为是否发生了变化,还是我错了?
        • @OrenMilman 天哪,可能吗?该评论来自几年前,可能是参考我当时使用的 R 2.15 / 2.14。
        【解决方案7】:

        除了此处实际传递您的对象的其他答案(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)
          }
        }
        

        然后我们可以声明“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)访问“按引用”变量,您将从外部获取它们尚未修改的值。此外,此实现仅将简单的变量名称作为参数处理,因此诸如 f(x[1], ...) 之类的索引参数将不起作用(尽管您可以通过更复杂的表达式操作来实现它以避开有限的 assign)。

        【讨论】:

          【解决方案8】:

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

          【讨论】:

          • R6 是一个用户贡献的包。引用类是 R(或者更确切地说是它的方法包)附带的东西。 R6 类似于参考类,正如文档所说:“R6 类类似于 R 的标准参考类”。
          【解决方案9】:

          除了其他建议之外,您还可以编写 C/C++ 函数,通过引用获取参数并就地工作,并在 R 中直接调用它们,这要感谢Rcpp(以及其他)。 具体见this answer

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 2010-12-17
            • 2022-06-17
            • 2011-06-14
            • 1970-01-01
            • 2020-06-25
            • 1970-01-01
            • 2015-09-15
            相关资源
            最近更新 更多