R: copy/move one environment to another

前端 未结 7 2000
-上瘾入骨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:57

    The other current solutions who actually attempt to make a copy will all fail if the environment contains promises, because they convert environments to lists.

    The solution below works in these cases. Following @geoffrey-poole 's idea I propose an argument to deep copy or not, and I showcase the function on a test case.

    It uses the unexported function is_promise2() from te package {pryr}. I don't know of a base R equivalent.

    The function

    clone_env <- function(env, deep = FALSE) {
      # create new environment with same parent
      clone <- new.env(parent = parent.env(env))
      for(obj in ls(env, all.names = TRUE)) {
        promise_lgl <- pryr:::is_promise2(as.symbol(obj), env = env)
        if(promise_lgl) {
          # fetch promise expression, we use bquote to feed the right unquoted
          # value to substitute
          promise_expr <- eval(bquote(substitute(.(as.symbol(obj)), env = env)))
          # Assign this expression as a promise (delayed assignment) in our
          # cloned environment
          eval(bquote(
            delayedAssign(obj, .(promise_expr), eval.env = env, assign.env = clone)))
        } else {
          obj_val <- get(obj, envir = env)
          if(is.environment(obj_val) && deep) {
            assign(obj, clone_env(obj_val, deep = TRUE),envir= clone)
          } else  {
            assign(obj, obj_val, envir= clone)
          }
        }
      }
      attributes(clone) <- attributes(env)
      clone
    }
    

    Shallow copy

    Let's build an environment containing a character variable, a promise (note that a is undefined), and a nested environment.

    create_test_env <- function(x = a){
      y <- "original"
      nested_env <- new.env()
      nested_env$nested_value <- "original"
      environment()
    }
    env <- create_test_env()
    ls(env)
    #> [1] "nested_env" "x"          "y"
    
    # clone it, with deep = FALSE
    shallow_clone <- clone_env(env, deep = FALSE) 
    #> Registered S3 method overwritten by 'pryr':
    #>   method      from
    #>   print.bytes Rcpp
    ls(shallow_clone)
    #> [1] "nested_env" "x"          "y"
    
    # the promise was copied smoothly
    a <- 42
    shallow_clone$x
    #> [1] 42
    
    # We can change values independently
    shallow_clone$y <- "modified"
    env$y
    #> [1] "original"
    
    # except if we have nested environents!
    shallow_clone$nested_env$nested_value <- "modified"
    env$nested_env$nested_value
    #> [1] "modified"
    

    Deep copy

    Let's do it all over again, but with a deep clone now, we see the nested values are distinct this time.

    env <- create_test_env()
    deep_clone <- clone_env(env, deep = TRUE) 
    a <- 42
    deep_clone$x
    #> [1] 42
    deep_clone$y <- "modified"
    env$y
    #> [1] "original"
    deep_clone$nested_env$nested_value <- "modified"
    env$nested_env$nested_value
    #> [1] "original"
    

    Created on 2020-09-10 by the reprex package (v0.3.0)

提交回复
热议问题