21

我读了这个关于重命名对象的基本问题和@Shane 的回答,指出我懒惰的评估。现在我想知道是否assign也懒惰地评估。就像这里:

assign("someNewName",someOldObject)
rm(someOldObject)

我对此感到疑惑的原因是以下用例:假设我有 10K+ R 个对象,每个对象都有两个名为originalNameand的属性additionalName。现在我想编写一个函数,可以有效地让用户从一个名称切换到另一个名称,而不会丢失这两个属性。大概是这样...

编辑:根据@Hadley 的输入,我更改了我的代码。

switchObjectName <- function(x) {
  n1 <- attributes(x)$originalName
  n2 <- attributes(x)$additionalName
  objName <- deparse(substitute(x))
  if(objName == n1) {
    delayedAssign(n2,x,assign.env=.GlobalEnv)
  } else {
    delayedAssign(n1,x,assign.env=.GlobalEnv)
  }
  rm(list=c(objName),envir=.GlobalEnv)    
}

这很好用,但我很难得到rm正确的陈述。我试过rm(objName,envir=.GlobalEnv)但无法让它工作,尽管 objName 绝对是一个字符,因为它是deparse(substitute(x).

4

1 回答 1

6

R 语言通常具有值语义。该分配x <- y意味着x并且y将是同一对象的独立副本(更新将是独立的)yx一个天真的实现x <- y总是会为其分配内存x并完全复制y到其中。相反,GNU-R 使用写时复制机制,它会将复制推迟到实际发生更新,这样可以节省内存/执行时间,以防万一它没有发生。R 用户不必知道这种优化,它是完全透明的(除了一些罕见的情况,如内存不足错误)。这种机制适用于写成x <- yassign("x", y)同等的作业。

惰性求值是语言设计的一部分,对 R 用户/程序员可见。作为参数传递给函数的表达式,例如在foo(ls())传递的表达式 isls()中,仅在被调用函数的实现需要时才被延迟评估。

delayedAssign是一个低级函数,对 R 用户/程序员可见,但它实际上仅用于包的延迟加载,不应在用户程序中使用。delayedAssign允许指定一个表达式来计算变量的值;仅当/当第一次读取变量时,计算才会延迟发生。

因此,要回答这个问题,R 中的赋值总是“惰性”的,因为它使用了写时复制机制。赋值右侧的计算也可以是惰性的(使用delayedAssign),但用户程序不应该需要/使用它。

我认为对于变量的“重命名”,没有必要使用delayedAssign(因为不计算右侧)。它只会使情况变得更加复杂,并且由于必须进行簿记,因此可能会产生性能开销delayedAssign。如果我必须重命名变量,我只会使用普通赋值。

为了代码清晰,我也会尽可能避免从环境中删除变量,甚至从函数分配到全局环境中,例如,我只会创建一个新列表并将新绑定(变量)插入其中。

提到了写时复制机制,在 GNU-R 中的当前实现中,任何所描述的解决方案都可能导致内存复制,如果没有重命名变量,这将是不必要的。在 R 级别无法避免这种情况。

于 2016-10-17T10:32:06.653 回答