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

kuuvgm7e  于 2023-10-13  发布在  其他
关注(0)|答案(7)|浏览(190)

我想问一下,是否有可能将一个环境中的所有对象同时复制/移动到另一个环境中。举例来说:

  1. f1 <- function() {
  2. print(v1)
  3. print(v2)
  4. }
  5. f2 <- function() {
  6. v1 <- 1
  7. v2 <- 2
  8. # environment(f1)$v1 <- v1 # It works
  9. # environment(f1)$v2 <- v2 # It works
  10. environment(f1) <- environment(f2) # It does not work
  11. }
  12. f2()
  13. f1()
6qfn3psc

6qfn3psc1#

你至少可以做三件不同的事情:
1.克隆环境(创建完全相同的副本)
1.将一个环境的内容复制到另一个环境
1.共享同一个环境
要克隆,请执行以下操作:

  1. # Make the source env
  2. e1 <- new.env()
  3. e1$foo <- 1
  4. e1$.bar <- 2 # a hidden name
  5. ls(e1) # only shows "foo"
  6. # This will clone e1
  7. e2 <- as.environment(as.list(e1, all.names=TRUE))
  8. # Check it...
  9. identical(e1, e2) # FALSE
  10. e2$foo
  11. e2$.bar

要复制内容,您可以按照@gsk显示的操作。但是,all.names标志仍然很有用:

  1. # e1 is source env, e2 is dest env
  2. for(n in ls(e1, all.names=TRUE)) assign(n, get(n, e1), e2)

分享环境就是@koshke所做的。这通常可能更有用。结果与创建一个局部函数相同:

  1. f2 <- function() {
  2. v1 <- 1
  3. v2 <- 2
  4. # This local function has access to v1 and v2
  5. flocal <- function() {
  6. print(v1)
  7. print(v2)
  8. }
  9. return(flocal)
  10. }
  11. f1 <- f2()
  12. f1() # prints 1 and 2
展开查看全部
avwztpqn

avwztpqn2#

试试这个:

  1. f2 <- function() {
  2. v1 <- 1
  3. v2 <- 2
  4. environment(f1) <<- environment()
  5. }
gpnt7bae

gpnt7bae3#

如果环境中包含promise,其他当前的解决方案实际上试图进行复制,都将失败,因为它们将环境转换为列表。
{rlang}现在有env_clone(),它将克隆环境,包括promise和活动绑定。它不做深拷贝,但默认情况下克隆环境与一个新的父,你可以做rlang::env_clone(env, parent.env(env))保持相同的父。
我推荐上面的,在下面找到以前的解决方案,当{rlang}有更多的限制。它的行为应该类似,但提供了一个深复制的选项,并在底部给出了警告。
下面的解决方案适用于这些情况。根据@ geoffrey-poole的想法,我提出了一个关于是否深度复制的参数,并在一个测试用例中展示了这个函数。
它使用 {pryr} 包中未导出的函数。我不知道有R基的等价物。

函数

  1. clone_env <- function(env, deep = FALSE) {
  2. is_unevaled_promise <- function(name, env) {
  3. pryr:::is_promise2(name, env) && !pryr:::promise_evaled(name, env)
  4. }
  5. # create new environment with same parent
  6. clone <- new.env(parent = parent.env(env))
  7. for(obj in ls(env, all.names = TRUE)) {
  8. promise_lgl <- is_unevaled_promise(as.symbol(obj), env = env)
  9. if(promise_lgl) {
  10. # fetch promise expression and env
  11. promise_expr <- pryr:::promise_code(obj, env)
  12. promise_env <- pryr:::promise_env(obj, env)
  13. # Assign this expression as a promise (delayed assignment) in our
  14. # cloned environment
  15. eval(bquote(
  16. delayedAssign(obj, .(promise_expr), eval.env = promise_env, assign.env = clone)))
  17. } else {
  18. obj_val <- get(obj, envir = env)
  19. if(is.environment(obj_val) && deep) {
  20. assign(obj, clone_env(obj_val, deep = TRUE),envir= clone)
  21. } else {
  22. assign(obj, obj_val, envir= clone)
  23. }
  24. }
  25. }
  26. attributes(clone) <- attributes(env)
  27. clone
  28. }

浅拷贝

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

  1. create_test_env <- function(x = a){
  2. y <- "original"
  3. nested_env <- new.env()
  4. nested_env$nested_value <- "original"
  5. environment()
  6. }
  7. env <- create_test_env()
  8. ls(env)
  9. #> [1] "nested_env" "x" "y"
  10. # clone it, with deep = FALSE
  11. shallow_clone <- clone_env(env, deep = FALSE)
  12. #> Registered S3 method overwritten by 'pryr':
  13. #> method from
  14. #> print.bytes Rcpp
  15. ls(shallow_clone)
  16. #> [1] "nested_env" "x" "y"
  17. # the promise was copied smoothly
  18. a <- 42
  19. shallow_clone$x
  20. #> [1] 42
  21. # We can change values independently
  22. shallow_clone$y <- "modified"
  23. env$y
  24. #> [1] "original"
  25. # except if we have nested environents!
  26. shallow_clone$nested_env$nested_value <- "modified"
  27. env$nested_env$nested_value
  28. #> [1] "modified"

深度复制

让我们从头再来一遍,但是现在使用深度克隆,我们看到这次嵌套的值是不同的。

  1. env <- create_test_env()
  2. deep_clone <- clone_env(env, deep = TRUE)
  3. a <- 42
  4. deep_clone$x
  5. #> [1] 42
  6. deep_clone$y <- "modified"
  7. env$y
  8. #> [1] "original"
  9. deep_clone$nested_env$nested_value <- "modified"
  10. env$nested_env$nested_value
  11. #> [1] "original"

创建于2020-09-10由reprex package(v0.3.0)
请注意,深度副本并不是超级健壮的:

  • 我们应该递归地探索每个列表对象,并寻找环境来克隆。
  • 如果我们在那里找到.GlobalEnv.BaseNamespaceEnv等,我们真的想克隆它们吗?我们可以有一个参数来提供参数,而不是克隆
  • 属性也可能包含环境,并且这些不会被深度复制

我还没有满足在兔子洞里走得更远的需要。我你需要这个随时编辑或复制和改进成自己的答案。

展开查看全部
cuxqih21

cuxqih214#

e1包含引用其他环境的名称时,Tommy发布的“clone”方法不会进行真正的(深度)克隆。例如,如果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'方法使用rapplyenvir内的嵌套环境上递归调用cloneEnv,其级别与环境嵌套的级别相同。所以,最后,它递归地调用了rapply,这有点让人费解,但工作得很好。
请注意,如果嵌套环境包含引用父环境的名称,则使用“deep”方法将永远不会从递归调用中返回。如果我能想出一个方法来检查这个,我会包括它...
还要注意,环境可以有属性,因此复制属性对于真正的克隆是必要的,这个解决方案也解决了这个问题。

  1. cloneEnv <- function(envir, deep = T) {
  2. if(deep) {
  3. clone <- list2env(rapply(as.list(envir, all.names = TRUE), cloneEnv, classes = "environment", how = "replace"), parent = parent.env(envir))
  4. } else {
  5. clone <- list2env(as.list(envir, all.names = TRUE), parent = parent.env(envir))
  6. }
  7. attributes(clone) <- attributes(envir)
  8. return(clone)
  9. }

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

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

显示foobar的值:

  1. e1$foo
  2. [1] "Christmas"
  3. e1$nestedEnv$bar
  4. [1] "New Years"

创建一个深度克隆(即,e2包含nestedEnv的副本)

  1. e2 <- cloneEnv(e1, deep = TRUE)

e1中的nestedEnv引用的环境与e2中的nestedEnv不同:

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

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

  1. e2$foo
  2. [1] "Christmas"
  3. e2$nestedEnv$bar
  4. [1] "New Years"

更改e2中的值:

  1. e2$foo <- "Halloween"
  2. e2$nestedEnv$bar <- "Thanksgiving"

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

  1. e1$foo
  2. [1] "Christmas"
  3. e2$foo
  4. [1] "Halloween"
  5. e1$nestedEnv$bar
  6. [1] "New Years"
  7. e2$nestedEnv$bar
  8. [1] "Thanksgiving"

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

  1. e2 <- cloneEnv(e1, deep = FALSE)

e2中的nestedEnve1中的nestedEnv指向相同的环境:

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

更新e2e2nestedEnv中的值:

  1. e2$foo <- "Halloween"
  2. e2$nestedEnv$bar <- "Thanksgiving"

foo的值是独立的:

  1. e1$foo
  2. [1] "Christmas"
  3. e2$foo
  4. [1] "Halloween"

但是更新值e2bar也更新了e1bar,因为e1$nestedEnve2$nestedEnv引用(指向)相同的环境。

  1. e1$nestedEnv$bar
  2. [1] "Thanksgiving"
  3. e2$nestedEnv$bar
  4. [1] "Thanksgiving"
展开查看全部
vlju58qv

vlju58qv5#

你可以使用assign:

  1. f1 <- function() {
  2. print(v1)
  3. print(v2)
  4. }
  5. f2 <- function() {
  6. v1 <- 1
  7. v2 <- 2
  8. for(obj in c("v1","v2")) {
  9. assign(obj,get(obj),envir=f1.env)
  10. }
  11. }

如果不想列出对象,ls()接受一个环境参数。
你必须弄清楚如何让f1.env成为指向f1内部的环境:-)

展开查看全部
3zwjbxry

3zwjbxry6#

我在我的包中使用这个函数来复制对象:

  1. copyEnv <- function(from, to, names=ls(from, all.names=TRUE)) {
  2. mapply(assign, names, mget(names, from), list(to),
  3. SIMPLIFY = FALSE, USE.NAMES = FALSE)
  4. invisible(NULL)
  5. }
fdbelqdn

fdbelqdn7#

要做到这一点:

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

打开f1环境并运行以下命令:

  1. ls(load(f2))

相关问题