我想问一下,是否有可能将一个环境中的所有对象同时复制/移动到另一个环境中。举例来说:
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()
7条答案
按热度按时间6qfn3psc1#
你至少可以做三件不同的事情:
1.克隆环境(创建完全相同的副本)
1.将一个环境的内容复制到另一个环境
1.共享同一个环境
要克隆,请执行以下操作:
要复制内容,您可以按照@gsk显示的操作。但是,
all.names
标志仍然很有用:分享环境就是@koshke所做的。这通常可能更有用。结果与创建一个局部函数相同:
avwztpqn2#
试试这个:
gpnt7bae3#
如果环境中包含promise,其他当前的解决方案实际上试图进行复制,都将失败,因为它们将环境转换为列表。
{rlang}现在有
env_clone()
,它将克隆环境,包括promise和活动绑定。它不做深拷贝,但默认情况下克隆环境与一个新的父,你可以做rlang::env_clone(env, parent.env(env))
保持相同的父。我推荐上面的,在下面找到以前的解决方案,当{rlang}有更多的限制。它的行为应该类似,但提供了一个深复制的选项,并在底部给出了警告。
下面的解决方案适用于这些情况。根据@ geoffrey-poole的想法,我提出了一个关于是否深度复制的参数,并在一个测试用例中展示了这个函数。
它使用 {pryr} 包中未导出的函数。我不知道有R基的等价物。
函数
浅拷贝
让我们构建一个包含字符变量、promise(注意
a
是未定义的)和嵌套环境的环境。深度复制
让我们从头再来一遍,但是现在使用深度克隆,我们看到这次嵌套的值是不同的。
创建于2020-09-10由reprex package(v0.3.0)
请注意,深度副本并不是超级健壮的:
.GlobalEnv
,.BaseNamespaceEnv
等,我们真的想克隆它们吗?我们可以有一个参数来提供参数,而不是克隆我还没有满足在兔子洞里走得更远的需要。我你需要这个随时编辑或复制和改进成自己的答案。
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'方法使用
rapply
在envir
内的嵌套环境上递归调用cloneEnv
,其级别与环境嵌套的级别相同。所以,最后,它递归地调用了rapply
,这有点让人费解,但工作得很好。请注意,如果嵌套环境包含引用父环境的名称,则使用“deep”方法将永远不会从递归调用中返回。如果我能想出一个方法来检查这个,我会包括它...
还要注意,环境可以有属性,因此复制属性对于真正的克隆是必要的,这个解决方案也解决了这个问题。
举个例子:
创建环境
e1
,其中还包含一个嵌套环境:显示
foo
和bar
的值:创建一个深度克隆(即,
e2
包含nestedEnv
的副本)e1
中的nestedEnv
引用的环境与e2
中的nestedEnv
不同:但是值是相同的,因为
e2$nestedEnv
是e1$nestedEnv
的副本:更改
e2
中的值:同样,
e1
中的值保持不变,因为e1$nestedEnv
指向的环境与e2$nestedEnv
不同:现在,使用Tommy的方法重新创建
e1
:e2
中的nestedEnv
与e1
中的nestedEnv
指向相同的环境:更新
e2
和e2
的nestedEnv
中的值:foo
的值是独立的:但是更新值
e2
的bar
也更新了e1
的bar
,因为e1$nestedEnv
和e2$nestedEnv
引用(指向)相同的环境。vlju58qv5#
你可以使用assign:
如果不想列出对象,
ls()
接受一个环境参数。你必须弄清楚如何让f1.env成为指向f1内部的环境:-)
3zwjbxry6#
我在我的包中使用这个函数来复制对象:
fdbelqdn7#
要做到这一点:
打开
f1
环境并运行以下命令: