R:将一个环境复制/移动到另一个环境

Apo*_*ros 19 r

我想问一下是否可以将一个环境的所有对象复制/移动到另一个环境.例如:

f1 <- function() {
    print(v1)
    print(v2)
}

f2 <- function() {
    v1 <- 1
    v2 <- 2

    # environment(f1)$v1 <- v1         # It works
    # environment(f1)$v2 <- v2         # It works

    environment(f1) <- environment(f2) # It does not work
}

f2()
f1()
Run Code Online (Sandbox Code Playgroud)

TNX,提前

Tom*_*mmy 27

你可以做至少3种不同的事情:

  1. 克隆环境(创建完全重复的)
  2. 将一个环境的内容复制到另一个环境
  3. 共享相同的环境

要克隆:

# Make the source env
e1 <- new.env()
e1$foo <- 1
e1$.bar <- 2   # a hidden name
ls(e1) # only shows "foo"

# This will clone e1
e2 <- as.environment(as.list(e1, all.names=TRUE))

# Check it...
identical(e1, e2) # FALSE
e2$foo
e2$.bar
Run Code Online (Sandbox Code Playgroud)

要复制内容,您可以执行@gsk显示的内容.但同样,该all.names标志很有用:

# e1 is source env, e2 is dest env
for(n in ls(e1, all.names=TRUE)) assign(n, get(n, e1), e2)
Run Code Online (Sandbox Code Playgroud)

分享环境是@koshke所做的.这可能通常更有用.结果与创建本地函数的结果相同:

f2 <- function() {
  v1 <- 1 
  v2 <- 2

  # This local function has access to v1 and v2
  flocal <- function() {
    print(v1)
    print(v2)
  }

  return(flocal)
} 

f1 <- f2()
f1() # prints 1 and 2 
Run Code Online (Sandbox Code Playgroud)


koh*_*ske 9

试试这个:

f2 <- function() {
    v1 <- 1
    v2 <- 2
    environment(f1) <<- environment()
}
Run Code Online (Sandbox Code Playgroud)


Geo*_*ole 6

e1当包含引用其他环境的名称时,Tommy 发布的“克隆”方法不会进行真正的(深度)克隆。例如,如果e1$nestedEnv引用一个环境,e2$nestedEnv将引用相同的环境,而不是该环境的副本。因此,名称e1$nestedEnv$bar将引用相同的内存位置,并且e2$nestedEnv$bar分配给的任何新值也e1$nestedEnv$bar将被反映e2$nestedEnv$bar。这可能是理想的行为,但调用e2的克隆e1可能会产生误导。

这是一个函数,允许用户在复制任何嵌套环境的同时复制环境(“深度克隆”,使用deep = TRUE),或者仅使用 Tommy 提出的方法来复制环境,同时保留对的原始引用任何嵌套环境(使用deep = FALSE)。

'deep = TRUE' 方法用于rapply递归调用cloneEnv内的嵌套环境envir,其级别与环境的嵌套级别一样多。所以,最后,它递归地调用rapply,这有点令人费解,但效果很好。

请注意,如果嵌套环境包含引用父环境的名称,则使用“deep”方法将永远不会从递归调用中返回。如果我能找到一种方法来检查这一点,我会把它包括在内......

另请注意,环境可以具有属性,因此复制属性对于真正的克隆来说是必要的,该解决方案也解决了这个问题。

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)
}
Run Code Online (Sandbox Code Playgroud)

一个例子:

创建环境e1,其中还包含一个嵌套环境:

e1 <- new.env()
e1$foo <- "Christmas"
e1$nestedEnv <- new.env()
e1$nestedEnv$bar <- "New Years"
Run Code Online (Sandbox Code Playgroud)

foo显示和的值bar

e1$foo
[1] "Christmas"
e1$nestedEnv$bar
[1] "New Years"
Run Code Online (Sandbox Code Playgroud)

进行深度克隆(即e2包含复制nestedEnv

e2 <- cloneEnv(e1, deep = TRUE)
Run Code Online (Sandbox Code Playgroud)

nestedEnve1引用中的环境与nestedEnv在以下环境中不同e2

identical(e1$nestedEnv, e2$nestedEnv)
[1] FALSE
Run Code Online (Sandbox Code Playgroud)

但值是相同的,因为e2$nestedEnv是 的副本e1$nestedEnv

e2$foo
[1] "Christmas"
e2$nestedEnv$bar
[1] "New Years"
Run Code Online (Sandbox Code Playgroud)

更改以下值e2

e2$foo <- "Halloween"
e2$nestedEnv$bar <- "Thanksgiving"
Run Code Online (Sandbox Code Playgroud)

中的值e1再次保持不变,因为e1$nestedEnv指向与 不同的环境e2$nestedEnv

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

e1$nestedEnv$bar
[1] "New Years"
e2$nestedEnv$bar
[1] "Thanksgiving"
Run Code Online (Sandbox Code Playgroud)

e1现在,使用 Tommy 的方法重新创建:

e2 <- cloneEnv(e1, deep = FALSE)
Run Code Online (Sandbox Code Playgroud)

nestedEnvine2指向与nestedEnvin相同的环境e1

identical(e1$nestedEnv, e2$nestedEnv)
[1] TRUE
Run Code Online (Sandbox Code Playgroud)

e2更新和e2中的值nestedEnv

e2$foo <- "Halloween"
e2$nestedEnv$bar <- "Thanksgiving"
Run Code Online (Sandbox Code Playgroud)

的值foo是独立的:

e1$foo
[1] "Christmas"
e2$foo
[1] "Halloween"
Run Code Online (Sandbox Code Playgroud)

但更新 value也更新了e2' ,因为和引用(指向)相同的环境。bare1bare1$nestedEnve2$nestedEnv

e1$nestedEnv$bar
[1] "Thanksgiving"
e2$nestedEnv$bar
[1] "Thanksgiving"
Run Code Online (Sandbox Code Playgroud)


Ari*_*man 5

您可以使用assign:

f1 <- function() {
  print(v1)
  print(v2)
}

f2 <- function() {
  v1 <- 1
  v2 <- 2

  for(obj in c("v1","v2")) {
    assign(obj,get(obj),envir=f1.env)
  }
}
Run Code Online (Sandbox Code Playgroud)

如果您不想列出对象,请ls()使用环境参数.

你必须弄清楚如何让f1.env成为一个指向f1的环境:-)

  • 对于它的价值,这个答案似乎与我在R核心成员编写的R代码中内部完成的方式相匹配......所以它可能是"规范的".@ kohske很聪明,但这让我很紧张. (2认同)

Moo*_*per 5

如果环境包含承诺,实际尝试制作副本的其他当前解决方案都将失败,因为它们将环境转换为列表。

下面的解决方案适用于这些情况。遵循@geoffrey-poole 的想法,我提出了一个是否深复制的论点,并在测试用例中展示了该函数。

它使用is_promise2()te 包{pryr} 中未导出的函数。我不知道基本的 R 等价物。

功能

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
}
Run Code Online (Sandbox Code Playgroud)

浅拷贝

让我们构建一个包含字符变量、promise(注意a未定义)和嵌套环境的环境。

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"
Run Code Online (Sandbox Code Playgroud)

深拷贝

让我们再做一遍,但是现在有了深度克隆,我们看到这次嵌套的值是不同的。

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"
Run Code Online (Sandbox Code Playgroud)

reprex 包(v0.3.0)于 2020 年 9 月 10 日创建