Siniyas
Siniyas

Reputation: 471

save(), unexpected behavior

I'm trying to save the variables from a function call via save()

f <- function(x) {
  r <- x - g(x)
  r
}
g <- function(y) {
  r <- y * h(y)
  r
}
h <- function(z) {
  save(list = ls(all = TRUE), file = "hello.RData", envir = parent.frame())
  r <- log(z)
  if (r < 10)
    r^2
  else r^3
}

This however returns the error "object z not found", even though according to the documentation parent.frame() is the standard value for envir.

save(list = ls(all = TRUE), file = "hello.RData", envir = sys.frame())

Does not work either

save(list = ls(all = TRUE), file = "hello.RData")

This works. But it is kind of a mystery for me what envir actually is if not the standard value given in the documentation.

What is the reason for the error and why is the parent frame the standard enviroment to be looked at in save()?

Upvotes: 1

Views: 77

Answers (2)

bjoseph
bjoseph

Reputation: 2166

This question has to do with scoping. For an indepth description of scoping check out this article http://adv-r.had.co.nz/Functions.html#lexical-scoping

First, by specifying envir=parent.frame() you are selecting the global environment. This is because your function is not defined within another function or object. For example, see the difference between printing the parent.frame in the two different h functions i define below.

!> h <- function(z){
 +     print(parent.frame())
 + }
 >
 >
 > h(10)
 <environment: R_GlobalEnv>
 >
 > h <- function(z){
 +     b <- function(){
 +         print(parent.frame())}
 +     b()
 + }
 >
 > h()
 <environment: 0x10f06b3c0>

I believe you either want to nest your functions inside each other (as shown above with functions h and b) or declare a 'global variable'. This is done by the

<<-

command

for instance:

 > h <- function(z) {
 +         r <<- log(z)
 +         if (r < 10){
 +                 r <<- r^2}
 +         else r<<- r^3
 +     }
 > h(1)
 > r
 [1] 0
 > h(10)
 > r
 [1] 5.301898
 > h(10000)
!> r
 [1] 84.83037

Upvotes: 0

MrFlick
MrFlick

Reputation: 206242

The important thing to note is that there is a difference between where default values for functions are evaluated and where values that you pass in are evaluated. Consider

ff<-function(a, b=a+2) {print(b)}

ff(2);
# [1] 4
ff(2, a+2);
# Error in print(b) : object 'a' not found

Despite the fact that the "default" for b is a+2 we can't just pass in a+2 because it needs to be evaluated in a different context.

Here's another example

gg<-function(a) {
    ff(2, match.call());
}

ff<-function(a, b=match.call()) {
    print(b)
}

ff(2);
# ff(a = 2)
gg(2)
# gg(a = 2);

Despite the fact that gg passes the same "default value" to ff, we get a different behavior.

This is what's happening with save(..., envir=parent.frame()). When you call that function in h(), you are getting the frame from where h() was called, but if you don't specify an environment, when save calls parent.frame(), save will get the frame where save() was called from. If you want to explicily pass the current environment to save(), you could do

save(list = ls(all = TRUE), file = "hello.RData", envir=environment())

But this will basically do the same thing as not specifying the envir= parameter at all.

So the documentation is correct, it's just that the behavior is different depending who actually is calling parent.frame().

Upvotes: 2

Related Questions