我很难理解为什么我递归申请eval使用的尝试rapply不起作用.
我有一个嵌套的表达式列表:
# Some expressions
expr1 <- quote(x <- x + 9)
expr2 <- quote(x <- x/2)
expr3 <- quote(x <- x + 3)
expr4 <- quote(x <- x * 3)
# Generate a nested list of expressions
exprs <- list(expr1, list(expr2, expr3), expr4)
# Initialize x, and attempt to eval the expressions
x <- 1
rapply(exprs, eval, envir = globalenv())
# Returns: [1] 10 5 8 24, but x is not changed.
Run Code Online (Sandbox Code Playgroud)
很明显,递归有效,但它没有像我指定的那样评估全局环境中的表达式.根据这个答案,我把列表弄平了,可以eval用了lapply.
flatten <- function(x) {
repeat {
if(!any(vapply(x, is.list, logical(1)))) return(x)
x <- Reduce(c, x)
}
}
# Both change x to 24 in the global env, like desired.
lapply(flatten(exprs), eval, envir = globalenv())
lapply(flatten(exprs), eval, envir = parent.frame())
Run Code Online (Sandbox Code Playgroud)
据我了解rapply/ lapply评估自己的环境,就像任何其他的功能,然后返回一个值.有意义的是,我可以指定全局环境或父框架(因为父框架lapply应该是它所调用的环境 - 这里是全局环境.)
按照这个逻辑,我希望指定全局环境rapply.指定父框架应该失败(并且确实如此),因为我假设嵌套的调用深入到rapply由原始调用创建的环境中得到的评估rapply.
我在这里错过了什么?为什么rapply呼叫不会x在全球环境中发生变化?
文档中的注释rapply:
语义与lapply的细节不同:特别是在调用C代码之前评估参数.
请尝试以下方法来查看它们的含义:
rapply(list(quote(stop("error"))), function(x) x)
# Error in (function (x) : error
lapply(list(quote(stop("error"))), function(x) x)
# [[1]]
# stop("error")
Run Code Online (Sandbox Code Playgroud)
你可以尝试这个解决方法:
rapply(exprs, evalq, envir = globalenv()) # updated with Hadley's equivalent but cleaner version.
# [1] 10 5 8 24
x
# [1] 24
Run Code Online (Sandbox Code Playgroud)
正如您所指出的那样,x在rapply环境中进行评估,这就是结果有意义的原因,但实际的eval语句不是原始表达式,而是结果(即在第一次迭代中,10在全局环境中进行评估,而不是x <- x + 9) .通过使用substitute我们能够挽救原始表达.