16

I was wondering if there is anyway to get the environment of a declared variable. Say I already have declared a variable to an environment and want to use that variable's environment to declare a few more variables. Something like getEnv("variable")

lmo
  • 37,904
  • 9
  • 56
  • 69
Avinash
  • 2,521
  • 4
  • 21
  • 35
  • 1
    Why do you need this? Maybe we can suggest a better solution. I would be cautious with working like this, creating a web of environment with relations between them can be really hard to comprehend and debug. I would just use a set of functions, and pass any information they need through the input arguments. – Paul Hiemstra May 16 '13 at 09:21
  • 2
    You can use `find`, but only if the environment is `attach`ed to the search path. – James May 16 '13 at 09:33
  • 1
    I wanted some variables to have a certain environment. As a check, I wanted to see if two variables had the same environment. Something like a part of my RUnit test suite. I just thought that since we have an option of giving environments while using the assign function, there should be some easy way of getting an assigned environment too and I would have added that check that's all. Nothing mission critical. – Avinash May 16 '13 at 12:05
  • It's a very interesting question, but I believe that the environment is not a property of the variable; conversely, the environment contains a list of symbols attached. But it would be nice to have a function that can list all environments currently available. Internally, R must keep track of it, but I know of no function that gives this info. – Ferdinand.kraft May 17 '13 at 15:42

4 Answers4

10

Refer to: http://adv-r.had.co.nz/Environments.html#env-basics

library(pryr)
x <- 5
where("x")
#> <environment: R_GlobalEnv>
where("mean")
#> <environment: base>

The where function is described in the above website. It only finds the first environment the variable appears in, but could easily be modified to find all.

Keith
  • 101
  • 1
  • 3
8

You can get all objects in your workspace with ls(), so you can then check which of those are environments:

envirs <- ls()[sapply(ls(), function(x) is.environment(get(x)))]

I need to use get() there because ls() returns character names of objects rather than the objects themselves. Now given some object x, we want to find which environments it exists in. All we need to do is iterate through each environment in envirs, and check if they contain whatever object we're looking for. Something along the lines of (checking for a variable x):

sapply(envirs, function(e) 'x' %in% ls(envir=get(e)))

Here's a function to do all this:

getEnv <- function(x) {
  xobj <- deparse(substitute(x))
  gobjects <- ls(envir=.GlobalEnv)
  envirs <- gobjects[sapply(gobjects, function(x) is.environment(get(x)))]
  envirs <- c('.GlobalEnv', envirs)
  xin <- sapply(envirs, function(e) xobj %in% ls(envir=get(e)))
  envirs[xin] 
}

This is more or less the same as what I did outside the function. gobjects reads from ls(), this time explicitly checking the global environment .GlobalEnv, since it is now within a function.

envirs is the same as before, except now it will check .GlobalEnv as well. xin is storing the names of which environments x was found in. The line:

xobj <- deparse(substitute(x))

Allows object to be tested without quotes e.g. getEnv(x) versus getEnv('x'). That's a matter of preference though, you can change it to accept characters instead.


Here's a few tests.

x1 <- 1
getEnv(x1)
# ".GlobalEnv"

x2 <- 2.1
e2 <- new.env()
assign('x2', 2.2, e2)
getEnv(x2)
# ".GlobalEnv" "e2" 

e3 <- new.env()
assign('x3', 3, e3)
getEnv(x3)
# "e3"

This only checks environments created within .GlobalEnv. I'm sure you can work out how to extend it to search across more environments though if you need.

I'm surprised there isn't some in-built function for this. Or maybe there is and I don't know about it. I've never actually needed to do anything like this before so maybe it's not actually surprising.

Ciarán Tobin
  • 7,306
  • 1
  • 29
  • 45
  • Firstly, I'd add `all=TRUE` to your `ls` calls. Then I'd make `getEnv` search environments recursively. But still it doesn't check environments not bound to variables, such as environments created for closure evaluation. Example run this in the console: `f <- function(x){ hidden <- x; function(){hidden <<- hidden+1; hidden} };` `g <- f(0);` `as.list(environment(g))` – Ferdinand.kraft May 17 '13 at 15:37
  • 1
    Also, lists may have environments as elements, there may environments lurking in the attributes of any object etc. – Ferdinand.kraft May 17 '13 at 15:56
  • @MadScone Thanks for your answer and everyone else! Even I am surprised that there is no in built function for this. I ultimately decided to not have that check but thanks again for your time. – Avinash May 20 '13 at 05:37
5

How about this:

getEnvOf <- function(what, which=rev(sys.parents())) {
  for (frame in which)
    if (exists(what, frame=frame, inherits=FALSE)) 
      return(sys.frame(frame))
  return(NULL)
}

Then we can:

x <- 1
getEnvOf("x")
# <environment: R_GlobalEnv>

getEnvOf("y")
# NULL

f <- function() getEnvOf("x")
f()
# <environment: R_GlobalEnv>

g <- function() { x <- 2; getEnvOf("x") }
g()
# <environment: 0x114c26518>
codeola
  • 838
  • 6
  • 14
  • 1
    Good function answering the question. Beyond the scope of the question there seem to be some limitations to be aware of, e. g. `warning("oops");getEnvOf("last.warning")` results in `NULL` whereas `pryr::where("last.warning")` results in ``. Your function does search within the search path of attached packages but in frames of the current call stack (as already mention, this was not asked for in the question but other users should be aware of this). – R Yoda Nov 27 '16 at 12:23
  • Works really well! I added "capture.output()" to the return statement to only get the desired string! – Squeezie Oct 22 '19 at 10:12
0

You can use find, as already suggested in the notes, to search the environment of an object in the searchpath. In case you want to seach in the Function Call Stack you can use exists to look in the sys.frame's.

findFrame <- function(what) {
  n <- sys.nframe()-1
  Filter(Negate(is.null), lapply(n:0, function(i) {
    if(exists(what, sys.frame(i), inherits=FALSE)) sys.frame(i)}))
}

x <- 0
f1 <- function() {
  x <- 1
  f2()
}
f2 <- function() {
  x <- 2
  tt  <- find("x")
  print(sapply(tt, as.environment))
  tt <- findFrame("x")
  print(tt)
}
a <- new.env(parent=emptyenv())
a$x <- 3
attach(a)
b <- new.env(parent=emptyenv())
b$x <- 4

find("x")
#[1] ".GlobalEnv" "a"

findFrame("x")
#[[1]]
#<environment: R_GlobalEnv>

f1()
#$.GlobalEnv
#<environment: R_GlobalEnv>
#
#$a
#<environment: 0x5576b0c7bb80>
#attr(,"name")
#[1] "a"
#
#[[1]]
#<environment: 0x5576af2fa1f8>
#
#[[2]]
#<environment: 0x5576aedcab20>
#
#[[3]]
#<environment: R_GlobalEnv>

f2()
#$.GlobalEnv
#<environment: R_GlobalEnv>
#
#$a
#<environment: 0x5576b0c7bb80>
#attr(,"name")
#[1] "a"
#
#[[1]]
#<environment: 0x5576b013bef0>
#
#[[2]]
#<environment: R_GlobalEnv>
GKi
  • 37,245
  • 2
  • 26
  • 48