I am having a hard time understanding why my attempt to recursively apply eval
, using rapply
, is not working.
I have a nested list of expressions:
# 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.
Clearly the recursion worked, but it did not evaluate the expression in the global environment, like I specified. Based on this answer I flattened out the list, and can eval
using 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())
As far as I understand rapply
/lapply
evaluate in their own environment, just like any other function, then return a value. It makes sense that I can specify the global environment, or the parent frame (because the parent frame of lapply
should be the environment it was called in - here the global environment.)
Following this logic I would expect specifying the global environment with rapply
to work. Specifying the parent frame should fail (and it does), because I am assuming the calls nested one deep within rapply
get evaluated in the environment created by the original call to rapply
.
What am I missing here? Why doesn't the rapply
call change x
in the global environment?
Note from documentation for rapply
:
The semantics differ in detail from lapply: in particular the arguments are evaluated before calling the C code.
Try the following to see what they mean:
rapply(list(quote(stop("error"))), function(x) x)
# Error in (function (x) : error
lapply(list(quote(stop("error"))), function(x) x)
# [[1]]
# stop("error")
You can try this as a workaround:
rapply(exprs, evalq, envir = globalenv()) # updated with Hadley's equivalent but cleaner version.
# [1] 10 5 8 24
x
# [1] 24
As you noted, x
is getting evaluated in the rapply
environment, which is why the result makes sense, but the actual eval
statement is not of your original expression, but of the result (i.e. in the first iteration, 10
gets evaluated in the global env, not x <- x + 9
). By using substitute
we're able to salvage the original expression.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With