我想问一下是否可以将一个环境的所有对象一次性复制/移动到另一个环境中。例如:
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()
TNX,提前
答案 0 :(得分:25)
你可以做至少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
要复制内容,您可以执行@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)
分享环境是@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
答案 1 :(得分:9)
试试这个:
f2 <- function() {
v1 <- 1
v2 <- 2
environment(f1) <<- environment()
}
答案 2 :(得分: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)
}
}
如果您不想列出对象,ls()
将采用环境参数。
你必须弄明白如何让f1.env成为一个指向f1的环境: - )
答案 3 :(得分:3)
如果环境包含promise,则其他实际尝试复制的当前解决方案都将失败,因为它们会将环境转换为列表。
在以下情况下,下面的解决方案有效。按照@ geoffrey-poole的想法,我提出了是否进行深度复制的论点,并在测试用例上展示了该功能。
它使用软件包 {pryr} 中未导出的函数is_promise2()
。我不知道基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
}
浅拷贝
让我们构建一个包含字符变量,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"
深拷贝
让我们再做一次,但是现在有了一个深层的克隆,我们看到嵌套值这次是不同的。
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"
由reprex package(v0.3.0)于2020-09-10创建
答案 4 :(得分:2)
我在包中使用此功能来复制对象:
copyEnv <- function(from, to, names=ls(from, all.names=TRUE)) {
mapply(assign, names, mget(names, from), list(to),
SIMPLIFY = FALSE, USE.NAMES = FALSE)
invisible(NULL)
}
答案 5 :(得分:1)
要做到这一点:
environment(f1) <- environment(f2) # It does not work
打开f1
环境,然后运行:
ls(load(f2))
答案 6 :(得分:0)
当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)
}
一个例子:
创建环境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)
nestedEnv
中的 e1
引用的环境不同于nestedEnv
中的e2
:
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"
现在,使用汤米的方法重新创建e1
:
e2 <- cloneEnv(e1, deep = FALSE)
nestedEnv
中的 e2
指向与nestedEnv
中的e1
相同的环境:
identical(e1$nestedEnv, e2$nestedEnv)
[1] TRUE
更新e2
和e2
的{{1}}中的值:
nestedEnv
e2$foo <- "Halloween"
e2$nestedEnv$bar <- "Thanksgiving"
的值是独立的:
foo
但是更新值e1$foo
[1] "Christmas"
e2$foo
[1] "Halloween"
的{{1}}也更新了e2
的{{1}},因为bar
和e1
引用(指向)相同的环境。
bar