R: copy/move one environment to another

前端 未结 7 2003
-上瘾入骨i
-上瘾入骨i 2020-12-14 02:01

I would like to ask if it is possible to copy/move all the objects of one environment to another, at once. For example:

f1 <- function() {
    print(v1)
          


        
7条回答
  •  感情败类
    2020-12-14 02:54

    The "clone" method posted by Tommy won't make a true (deep) clone when e1 contains names that reference other environments. For instance, if e1$nestedEnv references an environment, e2$nestedEnv will reference the same environment, not a copy of that environment. Thus, the name e1$nestedEnv$bar will reference the same memory location as e2$nestedEnv$bar and any new value assigned to e1$nestedEnv$bar will be reflected for e2$nestedEnv$bar as well. This may be desirable behavior, but calling e2 a clone of e1 could be misleading.

    Here is a function that will allow the user to make either a copy of an environment while also copying any nested environments (a "deep clone", using deep = TRUE), or just use the method proposed by Tommy to copy the environment while maintaining original references to any nested environments (using deep = FALSE).

    The 'deep = TRUE' method uses rapply to recursively call cloneEnv on nested environment within envir, for as many levels as the environments are nested. So, in the end, it recursively calls rapply, which is a bit of a mind bender, but works pretty well.

    Note that if a nested environment contains a name that references a parent environment, using the "deep" method will never return from the recursive calls. If I could have figured out a way to check for this, I would have included it...

    Note, too, that environments can have attributes, so copying the attributes would be necessary for a true clone, which this solution also addresses.

    cloneEnv <- function(envir, deep = T) {
      if(deep) {
        clone <- list2env(rapply(as.list(envir, all.names = TRUE), cloneEnv, classes = "environment", how = "replace"), parent = parent.env(envir))
      } else {
        clone <- list2env(as.list(envir, all.names = TRUE), parent = parent.env(envir))
      }
      attributes(clone) <- attributes(envir)
      return(clone)
    }
    

    An example:

    Create environment e1, which also contains a nested environment:

    e1 <- new.env()
    e1$foo <- "Christmas"
    e1$nestedEnv <- new.env()
    e1$nestedEnv$bar <- "New Years"
    

    Show the values for foo and bar:

    e1$foo
    [1] "Christmas"
    e1$nestedEnv$bar
    [1] "New Years"
    

    Make a deep clone (i.e. e2 contains makes a copy of nestedEnv)

    e2 <- cloneEnv(e1, deep = TRUE)
    

    nestedEnv in e1 references a difference environment than nestedEnv in e2:

    identical(e1$nestedEnv, e2$nestedEnv)
    [1] FALSE
    

    But the values are the same because e2$nestedEnv is a copy of e1$nestedEnv:

    e2$foo
    [1] "Christmas"
    e2$nestedEnv$bar
    [1] "New Years"
    

    Change the values in e2:

    e2$foo <- "Halloween"
    e2$nestedEnv$bar <- "Thanksgiving"
    

    And values in e1 remain unchanged, again, because e1$nestedEnv points to a different environment than e2$nestedEnv:

    e1$foo
    [1] "Christmas"
    e2$foo
    [1] "Halloween"
    
    e1$nestedEnv$bar
    [1] "New Years"
    e2$nestedEnv$bar
    [1] "Thanksgiving"
    

    Now, re-create e1 using Tommy's method:

    e2 <- cloneEnv(e1, deep = FALSE)
    

    nestedEnv in e2 points to the same environment as nestedEnv in e1:

    identical(e1$nestedEnv, e2$nestedEnv)
    [1] TRUE
    

    Update the values in e2 and e2's nestedEnv:

    e2$foo <- "Halloween"
    e2$nestedEnv$bar <- "Thanksgiving"
    

    Values of foo are independent:

    e1$foo
    [1] "Christmas"
    e2$foo
    [1] "Halloween"
    

    but updating value e2's bar has also updated e1's bar because e1$nestedEnv and e2$nestedEnv reference (point to) the same environment.

    e1$nestedEnv$bar
    [1] "Thanksgiving"
    e2$nestedEnv$bar
    [1] "Thanksgiving"
    

提交回复
热议问题