How to Reference the Local Environment Within a Function, in R

How can I reference the local environment within a function, in R?

To get the current environment, just call environment().

In general, sys.frame returns any of the environments currently on the call stack, and sys.nframe returns the current depth of the call stack. sys.frames returns a list of all environments on the call stack.

environment(f) returns the closure environment for a function f (where it will look for functions and global variables).

parent.env(e) returns the parent environment where it will look if a symbol is not found in e.

f <- function() {
function() list(curEnv=environment(), parent=parent.env(environment()),
grandParent=parent.env(parent.env(environment())), callStack=sys.frames(),
callStackDepth=sys.nframe())
}
g <- function(f, n=2) if (n>2) g(f, n-1) else f()

floc <- f() # generate a local function
g(floc, 3) # call it

This will call the local function floc with a stack depth of 3. It returns a list with the current environment, it's parent (the local environment in f), and it's grand parent (where f was defined, so globalenv). It also returns the list of stack frames (environments). These are the environments for the recursive calls in g (except the last one which is the current environment of floc).

R: How can I save all objects within a functions local environment?

Use the indicated save command.

outside = "not in function"
testFun <- function(){
a = 1
b = 2
c = 3
save(list = ls(all.names = TRUE), file = "environment.RData")
}
testFun()

load("environment.RData", e <- new.env())
ls(e)
## [1] "a" "b" "c"

Accessing variables in a function within a function

I do encourage you to read about lexical scoping,
but I think a good approach to avoid writing a lot of variables could be:

get_args_for <- function(fun, env = parent.frame(), inherits = FALSE, ..., dots) {
potential <- names(formals(fun))

if ("..." %in% potential) {
if (missing(dots)) {
# return everything from parent frame
return(as.list(env))
}
else if (!is.list(dots)) {
stop("If provided, 'dots' should be a list.")
}

potential <- setdiff(potential, "...")
}

# get all formal arguments that can be found in parent frame
args <- mget(potential, env, ..., ifnotfound = list(NULL), inherits = inherits)
# remove not found
args <- args[sapply(args, Negate(is.null))]
# return found args and dots
c(args, dots)
}

f_a <- function(b, c = 0, ..., d = 1) {
b <- b + 1
c(b = b, c = c, d = d, ...)
}

f_e <- function() {
b <- 2
c <- 2
arg_list <- get_args_for(f_a, dots = list(5))
do.call(f_a, arg_list)
}

> f_e()
b c d
3 2 1 5

Setting inherits = FALSE by default ensures that we only get variables from the specified environment.
We could also set dots = NULL when calling get_args_for so that we don't pass all variables,
but leave the ellipsis empty.

Nevertheless, it isn't entirely robust,
because dots is simply appended at the end,
and if some arguments are not named,
they could end up matched by position.
Also, if some values should be NULL in the call,
it wouldn't be easy to detect it.


I would strongly advise against using these below inside an R package.
Not only will it be rather ugly,
you'll get a bunch of notes from R's CMD check regarding undefined global variables.

Other options.

f_a <- function() {
return(b + c)
}

f_e <- function() {
b <- 2
c <- 2
# replace f_a's enclosing environment with the current evaluation's environment
environment(f_a) <- environment()
d <- f_a()
d
}

> f_e()
[1] 4

Something like the above probably wouldn't work inside an R package,
since I think a package's functions have their enclosing environments locked.

Or:

f_a <- function() {
with(parent.frame(), {
b + c
})
}

f_e <- function() {
b <- 2
c <- 2
f_a()
}

> f_e()
[1] 4

That way you don't modify the other function's enclosing environment permanently.
However, both functions will share an environment,
so something like this could happen:

f_a <- function() {
with(parent.frame(), {
b <- b + 1
b + c
})
}

f_e <- function() {
b <- 2
c <- 2
d <- f_a()
c(b,d)
}

> f_e()
[1] 3 5

Where calling the inner function modifies the values in the outer environment.

Yet another option that is a bit more flexible,
since it only modifies the enclosing environment temporarily by using eval.
However, there are certain R functions that detect their current execution environment through "daRk magic",
and cannot be fooled by eval;
see this discussion.

f_a <- function() {
b <- b + 1
b + c
}

f_e <- function() {
b <- 2
c <- 2
# use current environment as enclosing environment for f_a's evaluation
d <- eval(body(f_a), list(), enclos=environment())
c(b=b, d=d)
}

> f_e()
b d
2 5

Global and local variables in R

Variables declared inside a function are local to that function. For instance:

foo <- function() {
bar <- 1
}
foo()
bar

gives the following error: Error: object 'bar' not found.

If you want to make bar a global variable, you should do:

foo <- function() {
bar <<- 1
}
foo()
bar

In this case bar is accessible from outside the function.

However, unlike C, C++ or many other languages, brackets do not determine the scope of variables. For instance, in the following code snippet:

if (x > 10) {
y <- 0
}
else {
y <- 1
}

y remains accessible after the if-else statement.

As you well say, you can also create nested environments. You can have a look at these two links for understanding how to use them:

  1. http://stat.ethz.ch/R-manual/R-devel/library/base/html/environment.html
  2. http://stat.ethz.ch/R-manual/R-devel/library/base/html/get.html

Here you have a small example:

test.env <- new.env()

assign('var', 100, envir=test.env)
# or simply
test.env$var <- 100

get('var') # var cannot be found since it is not defined in this environment
get('var', envir=test.env) # now it can be found

do.call specify environment inside function

This seems to work, but i'm not sure if it has other implications I'm not considering:

fun_wrap1 <- function(){
funa1 <- function(x) x^2
funb1 <- function(x) x^3
lapply(c('funa1', 'funb1'), do.call, args=list(x=3), envir=environment())
}

fun_wrap1()
#[[1]]
#[1] 9
#
#[[2]]
#[1] 27

So this is essentially equivalent to having the lapply statement as:

lapply(
c('funa1', 'funb1'),
function(f) do.call(f, args=list(x=3), envir=environment() )
)

Look up local variable in function called by do.call

1) Since b is not defined in Foo, Foo will look for b in the environment in which Foo was defined, not the environment in which it was called.

You can redefine Foo's environment. This will make a copy of Foo such that free variables in it will be looked up in the local environment. No packages are used.

local({
b <- 3
environment(Foo) <- environment()
print(Foo(2))
print(do.call(Foo, list(a = 2)))

})
## [1] 5
## [1] 5

2) If it is ok to modify Foo then other possibilities are to modify it by passing b as an additional argument or passing an environment as an additional argument and have Foo evaluate b in that environment.

Foo2 <- function(a, b) {
return(a + b)
}
local({
b <- 3
print(Foo2(2, b))
print(do.call(Foo2, list(a = 2, b = b)))
})
## [1] 5
## [1] 5

3) or

Foo3 <- function(a, envir = parent.frame()) {
return(a + envir$b)
}
local({
b <- 3
print(Foo3(2))
print(do.call(Foo3, list(a = 2)))

})
## [1] 5
## [1] 5

4) A variation of the above that only involves modifying the signature of Foo but not its body is the following (or use get("b", parent.frame()) if you want to allow it to look into the ancestors of the parent frame as well).

Foo4 <- function(a, b = parent.frame()$b) {
return(a + b)
}
local({
b <- 3
print(Foo4(2))
print(do.call(Foo4, list(a = 2)))
})
## [1] 5
## [1] 5

5) Another approach is to inject a statement into Foo using trace and then remove it afterwards.

local({
b <- 3
on.exit(untrace(Foo))
trace(Foo, bquote(b <- .(b)), print = FALSE)
print(Foo(2))
print(do.call(Foo, list(a = 2)))
})
## [1] 5
## [1] 5

6) If we wrap the body of Foo in eval.parent(substitute({...})) that will effectively inject it into the caller giving it access to b. Also see Thomas Lumley article starting on page 11 of R News 1/3 .

Foo6 <- function(a) eval.parent(substitute({
return(a + b)
}))
local({
b <- 3
print(Foo6(2))
print(do.call(Foo6, list(a = 2)))
})
## [1] 5
## [1] 5

7) This is really the same as (6) under the hood except it wraps it nicely. This is the only one here that uses a package.

library(gtools)

Foo7 <- defmacro(a, expr = {
return(a + b)
})
local({
b <- 3
print(Foo7(2))
print(do.call(Foo7, list(a = 2)))
})
## [1] 5
## [1] 5

R Programming - creates variable in in the environment it was called

Try this:

createVariable <- function(var.name, var.value) {
assign(var.name,var.value,envir=parent.env(environment()))
}

Edit:
Some more details here and here.
With the initial solution, the variable is created in the global env because parent.env is the environment in which the function is defined and the createVariable function is defined in the global environment.

You might also want to try assign(var.name,var.value,envir=as.environment(sys.frames()[[1]])), which will create it in the highest test function calling createVariable in your example (first one on the call stack), in that case however, you will need to remove print(testVar) from testFunc when you call testFunc2 because the variable only be created in the environment of testFunc2, not testFunc. I don't know if that's what you mean by at the level at which it's called.

If you run this:

createVariable <- function(var.name, var.value) {
assign(var.name,var.value,envir=as.environment(sys.frames()[[1]]))
print("creating")
}

testFunc <- function() {
createVariable("testVar","test")
print("func1")
print(exists("testVar"))
}

testFunc2 <- function() {
testFunc()
print("func2")
print(exists("testVar"))
}

testFunc()
testFunc2()

You get

> testFunc()
[1] "creating"
[1] "func1"
[1] TRUE
> testFunc2()
[1] "creating"
[1] "func1"
[1] FALSE
[1] "func2"
[1] TRUE

Which means testVar is in testFun2's environment, not in testFunc's. Creating a new environment as others say might be safer.



Related Topics



Leave a reply



Submit