简体   繁体   中英

Do you have to define the environment of a function in R?

Do you have to explicitly define the environment of a custom function in R, or is it automatically "created" (defined?) when a function is created?

I want to know if creating a function automatically creates an environment within said function or whether the environment needs to be explicitly created. If the former is true (ie, the environment is automatically created), how do I explicitly refer to it?

Must I first create a new environment for said function (ie, by assigning a new environment using new.env ) or can I simply use some other syntax (perhaps something like " current.env() ")?

The only simple example I can come up with that would use this is assign functions inside the function.

function(a,b) {
  assign(paste(a,b,sep='.'), 'help me', envir = ONLY CURRENT FUNCTION)
}

The current environment is returned by environment() :

> environment()
<environment: R_GlobalEnv>
> foo <- function() environment()
> foo()
<environment: 0x1391b88>
> foo()
<environment: 0x1391f08>
> foo()
<environment: 0x1392288>
> foo()
<environment: 0x13916a0>

Notice that each call to foo() has a different environment as the environment you are thinking about only gets created when the function is called.

To get the enclosing frame, where the function was called from, use parent.frame() :

> bar <- function() parent.frame()
> bar()
<environment: R_GlobalEnv>

As bar() was called from my workspace, the global environment is returned. But, if we call bar() from inside another function, we get the environment of that function as that is now the calling environment:

> foobar <- function() { print(environment()); bar() }
> foobar()
<environment: 0x74a7d68>
<environment: 0x74a7d68>

If we look at your assign() example, you don't have to tell it which environment to use if you want the current environment as that is how it is setup to work by default. But, if you wanted to be explicit about this you'd use:

foo <- function(x) {
  assign("y", x, envir = environment())
  y # return(y) to show it is assigned `x`
}

Which gives

> foo(20)
[1] 20
> foo(10)
[1] 10
> foo("a")
[1] "a"

If you want to assign in the parent frame, just pass envir = parent.frame() :

foo <- function(x) {
  assign("y", x, envir = parent.frame())
  "not returning `y`" # return something else
}

which gives

> ls()
[1] "bar"    "foo"    "foobar"
> foo(20)
[1] "not returning `y`"
> ls()
[1] "bar"    "foo"    "foobar" "y"     
> y
[1] 20

Even though we didn't return y , the assign() created y in the environment we specified.

It is important to stress though that rarely should one be doing assign() like this, to assign into other environments. A major reason for this is that now your function has side effects and R is a functional language where it is easier to reason about how code works by having a function take inputs and return outputs, without affecting anything else. Hence you would prefer

foo1 <- function(x) {
  y <- x
  y
}

y <- foo1(x)

over

foo2 <- function(x, varname) {
  assign(varname, x, envir = parent.frame())
  invisible()
}

foo2(x, "y")

because I know that calling foo1() has no side effects, it's just taking some inputs and returning an output. This is a little contrived because you chose assign() but this would apply broadly. It's also the reason why complex lists or other types of objects are returned from many R functions, especially those fitting a model.

Don't mention an environment in the assign.

 foo = function(a,b){
  assign(paste(a,b,sep="."),99)
 return(hey.you) # note this is hard coded here...
}

There's nothing called hey.you :

> ls()
[1] "foo"

I call foo , which creates a hey.you and returns its value:

> foo("hey","you")
[1] 99

But doesn't mess up my environment because it was created in the function's environment:

> ls()
[1] "foo"

because that's the default.

?assign

    pos: where to do the assignment.  By default, assigns into the
          current environment.  See ‘Details’ for other possibilities.

If you HAD to mention it explicitly, and you don't have to, so don't, unless you can give us a real good reason why you have to, then just copy what assign does:

> foo = function(a,b){assign(paste(a,b,sep="."),99,envir=as.environment(-1));ls()}
> foo("fnord","fnord")
[1] "a"           "b"           "fnord.fnord"

Or copy what everyone else has been saying here:

> foo = function(a,b){assign(paste(a,b,sep="."),99,envir=environment());ls()}
> foo("bubble","gum")
[1] "a"          "b"          "bubble.gum"

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM