How can I customize the character search path when evaluating an expression?
In the case when I need to customize the search path for some characters. For example, when I evaluate an expression f(x,y,z)
, I need to evaluate it in the following chain of environments:
First, find the characters in env1
where x
and y
determined, but f
and z
are not.
then find f
and z
at env2
where both are defined.
The problem is that env1
both are env2
not self-defined, meaning their parent environment is determined when I can use them.
In this particular case, I can manually call exists
and get
to find those symbols. But this doesn't seem to work in the general case where the expression is in user input and is not known in advance.
Is there a general and quick way of working for any custom input expression, so that this expression evaluates within a series of a given environment with inherits = FALSE
the exception of the latter. Suppose the function is called evalc
, then the usage could be:
evalc(quote(f(x,y,z)), env1, env2, env3)
where a number of environments are given. For env1
and env2
symbols appear with inherits = FALSE
; but for the env3
characters are scanned with inherits = TRUE
.
source to share
1) Do you really need it inherits = FALSE
? This works differently:
esub <- function(...) do.call(substitute, list(...))
evalc <- function(expr, env1, env2, env3 = parent.frame()) {
eval(esub(esub(expr, env1), env2), env3)
}
# test
env1 <- list2env(list(x = 1, y = 2))
env2 <- list2env(list(f = function(...) list(...), z = 3))
evalc(quote(f(x, y, z)), env1, env2)
2) If you really need inherits = FALSE
, then we will copy each environment to a new environment, in which any empty environment will be the parent:
evalc <- function(expr, env1, env2, env3 = parent.frame()) {
e1 <- list2env(as.list(env1))
e2 <- list2env(as.list(env2))
parent.env(e1) <- parent.env(e2) <- emptyenv()
eval(esub(esub(expr, e1), e2), env3)
}
3) If we have an unknown number of media:
evalc <- function(expr, ..., env = parent.frame()) {
for(e in list(...)) {
ee <- list2env(as.list(e))
parent.env(ee) <- emptyenv()
expr <- esub(expr, ee)
}
eval(expr, env)
}
4) Here is another solution to avoid copying. This requires that you could change the parent item env1
, env2
...
evalc <- function(expr, ..., env = parent.frame()) {
for(e in list(...)) {
p <- parent.env(e)
parent.env(e) <- emptyenv()
expr <- esub(expr, e)
parent.env(e) <- p
}
eval(expr, env)
}
Update added 2, 3 and 4.
source to share