我有一个expr要评估的表达式;我需要评估它的符号/值对可能在三个环境中的一个(或多个!)中,我不确定是哪个。我想找到一种方便、有效的方式来“链接”环境。有没有办法在避免复制环境内容的同时安全地做到这一点?
这是设置:
env1 <- list2env(list(a=1))
env2 <- list2env(list(b=1))
env3 <- list2env(list(c=1))
expr <- quote(a+b)
Run Code Online (Sandbox Code Playgroud)
因此,我需要expr在环境和(但我不一定知道)的组合中进行评估。任何一个;; 并且会失败,因为这些环境中没有一个包含所有必需的符号。env1env2eval(expr, env1)eval(expr, env2)eval(expr,env3)
让我们假设我愿意承担这些符号无论是在env1+env2 或在env1+ env3。我可以:
问题:
parent.env()<-可能是一个坏主意 - 如中所述?parent.env:替换函数 parent.env<- 非常危险,因为它可用于以违反内部 C 代码所做假设的方式破坏性地改变环境。它可能会在不久的将来被删除。
(虽然,根据来源历史,关于“在不久的将来”移除的警告至少已经有 19 年的历史了......)
(事实上,我已经设法用这种方法引起了一些无限循环)
tryCatch(eval(call, envir=as.list(expr1), enclos=expr2),
error=function(e) {
tryCatch(eval(call, as.list(expr1), enclos=expr3))
Run Code Online (Sandbox Code Playgroud)
创造“环境中的环境”;一次尝试一对组合,看看哪一个有效。请注意,enclos= 只有在envir是 list 或 pairlist时才有效,这就是为什么我必须使用as.list().
问题:我想我最终还是将 的内容复制expr1到新环境中。
我可以使用嵌套更深的一组tryCatch()子句在我诉诸复制之前一次尝试一个环境,这将有助于避免在不必要的地方复制(但看起来很笨拙)。
将环境转换为列表,连接它们并将其用作eval. 请注意,这不会修改环境本身。
L <- do.call("c", lapply(list(env1, env2, env3), as.list))
eval(expr, L)
## [1] 2
Run Code Online (Sandbox Code Playgroud)
另请注意,这并不能复制的内容a,b和c。他们仍然在原来的地址:
library(pryr)
with(env1, address(a))
## [1] "0x2029f810"
with(L, address(a))
## [1] "0x2029f810"
Run Code Online (Sandbox Code Playgroud)
不,没有简单的方法来链接环境。如您所知,每个环境都有一个父环境,而父环境又是另一个环境,因此整体环境形成一个树形结构。(树的根是空的环境。)你不能轻易地从树上取下一片叶子并将其移植到另一片叶子上而不对其进行结构改变。
因此,如果您确实需要按照您描述的方式评估表达式,则必须解析它,自己查找名称,并将值替换到其中。但即使这样,最终也不一定会给您带来相同的价值,因为substitute()其中可能涉及类似的功能。
我的建议是重新开始,不要尝试做出像你所说的那样的表情。这可能涉及复制,但请记住,在 R 中复制通常很便宜:只有当您修改其中一个副本时才会产生成本。
编辑添加:
当前的其他四个答案隐含地做出了假设,即env1与env3您的示例中一样简单。如果这是真的,那么我会采用@G.Grothendieck 的解决方案。但在您的示例的这个简单变体中,所有操作都失败了:
env1 <- list2env(list(a=1))
env2parent <- list2env(list(b=1))
env2 <- new.env(parent = env2parent)
env3 <- list2env(list(c=1))
expr <- quote(a+b)
Run Code Online (Sandbox Code Playgroud)
我可以quote(b)使用进行评估eval(quote(b), envir = env2),但无法expr使用其他解决方案进行评估,除非我也包含env2parent在正在传递的环境列表中。
再次编辑:
这是一个基本上按照我的建议执行的解决方案,除了不进行解析之外,它使用all.vars@r2evans 答案之一中的函数。它的工作原理是将所有变量复制到一个公共环境中,因此会发生复制,但名称会保留:
envfunc3 <- function(expr, ...) {
vars <- all.vars(expr)
env <- new.env()
for (v in vars) {
for (e in list(...))
if (exists(v, envir = e)) {
assign(v, get(v, envir = e), envir = env)
break
}
}
eval(expr, envir=env)
}
Run Code Online (Sandbox Code Playgroud)