当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 在envir 内的嵌套环境中递归调用cloneEnv,因为嵌套的级别与嵌套的环境一样多。所以,最后,它递归地调用rapply,这有点让人费解,但效果很好。
请注意,如果嵌套环境包含引用父环境的名称,则使用“深度”方法将永远不会从递归调用中返回。如果我能想出一种方法来检查这个,我会把它包括在内......
还要注意,环境可以具有属性,因此复制属性对于真正的克隆是必要的,此解决方案也解决了这一问题。
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)
}
一个例子:
创建环境e1,其中还包含一个嵌套环境:
e1 <- new.env()
e1$foo <- "Christmas"
e1$nestedEnv <- new.env()
e1$nestedEnv$bar <- "New Years"
显示foo 和bar 的值:
e1$foo
[1] "Christmas"
e1$nestedEnv$bar
[1] "New Years"
进行深度克隆(即e2 包含复制nestedEnv)
e2 <- cloneEnv(e1, deep = TRUE)
e1 中的nestedEnv 引用了与e2 中的nestedEnv 不同的环境:
identical(e1$nestedEnv, e2$nestedEnv)
[1] FALSE
但值是相同的,因为e2$nestedEnv 是e1$nestedEnv 的副本:
e2$foo
[1] "Christmas"
e2$nestedEnv$bar
[1] "New Years"
更改e2中的值:
e2$foo <- "Halloween"
e2$nestedEnv$bar <- "Thanksgiving"
e1 中的值再次保持不变,因为e1$nestedEnv 指向的环境与e2$nestedEnv 不同:
e1$foo
[1] "Christmas"
e2$foo
[1] "Halloween"
e1$nestedEnv$bar
[1] "New Years"
e2$nestedEnv$bar
[1] "Thanksgiving"
现在,使用 Tommy 的方法重新创建 e1:
e2 <- cloneEnv(e1, deep = FALSE)
e2 中的nestedEnv 与e1 中的nestedEnv 指向相同的环境:
identical(e1$nestedEnv, e2$nestedEnv)
[1] TRUE
更新e2 和e2 的nestedEnv 中的值:
e2$foo <- "Halloween"
e2$nestedEnv$bar <- "Thanksgiving"
foo 的值是独立的:
e1$foo
[1] "Christmas"
e2$foo
[1] "Halloween"
但更新值e2 的bar 也更新了e1 的bar,因为e1$nestedEnv 和e2$nestedEnv 引用(指向)相同的环境。
e1$nestedEnv$bar
[1] "Thanksgiving"
e2$nestedEnv$bar
[1] "Thanksgiving"