Skip to content

changing the value of a variable from inside a function

6 messages · Tom, Michael Wolosin, Gabor Grothendieck +2 more

Tom
#
Michael,
I feel sure people are going to come up with other better suggestions
here but does <- work for you?
test <- matrix(NA,nrow=4,ncol=3)
test[1,] <- c(1,2,3)
blah <- function(i){
   test[i,] <<- c(0,1,2) + i
   return(test)
}
On Tue, 2005-15-11 at 12:22 -0500, Michael Wolosin wrote:
#
All -

I am trying to write R code to implement a recursive algorithm.  I've 
solved the problem in a klunky way that works, but uses  more memory and 
computing time than it should.

A more elegant solution than my current one would require updating the 
values of a variable that is located in what I will call the "root" 
environment - that environment from which the original call to the 
recursive function was issued.  Certainly, I could pass the variable into 
the function, update it inside, and return it.  However, the variable I am 
updating is a large matrix, and the recursion could end up several hundred 
levels deep.  Passing the matrix around would create a copy in the 
environment for each call, wasting memory, time, and space.

I've read the help on the "sys.{}" family of functions, and "eval", and 
although I can't claim to have absorbed it all, it seems like it is much 
easier to access the value of a variable in a parent frame than it is to 
update that value with assignment.
If you make an assignment inside a function, even if it is to a section of 
a variable that exists in a parent frame, the variable is only created or 
updated in the current environment - never in the parent frame.

For example:

test <- matrix(NA,nrow=4,ncol=3)
test[1,] <- c(1,2,3)
blah <- function(i){
   test[i,] <- c(0,1,2) + i
   return(test)
}
test
blah(2)
test

So the real question is, how do I write the function like "blah" above that 
updates "test" in the parent or root frame?

blah <- function(i){
   test[i,] <- c(1,2,3) + i  #modify this line somehow
   return(NULL)
}
If done "correctly", we will get:
 > blah(2)
 > test
       [,1] [,2] [,3]
  [1,]    1    2    3
  [2,]    2    3    4
  [3,]   NA   NA   NA
  [4,]   NA   NA   NA

And given an example that works from within a single function call, does it 
have to be modified to work recursively?

blah <- function(i){
   if (i<4) {blah(i + 1)}
   test[i,] <- c(0,1,2) + i  #modify this line somehow
   return(NULL)
}
If written "correctly", the following would be the output:
 > blah(2)
 > test
       [,1] [,2] [,3]
  [1,]    1    2    3
  [2,]    2    3    4
  [3,]    3    4    5
  [4,]    4    5    6

One idea would be to write out to a file.  The filename could reside in the 
root environment, and that is all that is needed.  But  this also seems 
inelegant (and slow).  If I can read and write to a file, I should be able 
to read and write to a memory location.

I suspect that the solution lies somewhere in the "sys" functions, but I 
was having trouble seeing it.  Any help would be appreciated.

Thank you in advance,

Mike
#
Use eval.parent as shown in example 1.  Note that you might
be tempted to use example 2 but it does not actually fulfill
the letter of the original post since it changes test in the lexical
environment of f, i.e.the environment where f is defined,
rather than the calling frame of f, i.e. the environment from where
f is called.  To get <<- to work with example 2 we must
create a new f that is the same as the original f but whose
lexical environment has been changed to be the caller frame
as shown in example 3.

# example 1.  ok.  test changed in caller frame.
test <- 11:13
f <- function(i) eval.parent(substitute(test[i] <- 99))
g <- function() { test <- 1:3; f(2); print(test) }
g()  # 1 99 3
test # 11 12 13

# example 2.  Same except f has been changed.
# Note that this changes test in the lexical environment
# rather than in the caller frame.
test <- 11:13
f <- function(i) test[i] <<- 99
g <- function() { test <- 1:3; f(2); print(test) }
g()  # 1 2 3
test  # 11 99 13

# example 3. same as example 2 but the lexical environment of f is
# forced to be the caller frame so that it works as in example 1.
# f is the same as in example 1 and g has been changed to
# create a new f like the original f but with the caller frame as its
# lexical environment.
test <- 11:13
f <- function(i) test[i] <<- 99
g <- function() { test <- 1:3; environment(f) <- environment(); f(2);
print(test) }
g()  # 1 99 3
test # 11 12 13


Another possibility, which is similar in effect to example 1, would be
to use defmacro in package gtools.
On 11/15/05, Michael Wolosin <msw10 at duke.edu> wrote:
#
You could use 'ref' package.

Feng
1 day later
#
On 11/15/2005 12:22 PM, Michael Wolosin wrote:
That's tricky and ugly, but possible in various ways.  However, the 
clean easy way to do this is to wrap your recursive function in a 
non-recursive one, and refer to variables in the non-recursive one using 
lexical scoping.  For example,

wrapper <- function(test) {
    test <- test  # make a copy in the wrapper environment
    blah <- function() {
        # references here to test will see the one in wrapper
        # blah can call itself; each invocation will see the same test

        test[i,] <<- expr  #  use "super-assignment" to modify it
    }
    return(test)
}

This makes one copy of the matrix and works on that.  If you want to 
make zero copies, you need to get tricky.

Duncan Murdoch

Certainly, I could pass the variable into
#
On 11/17/2005 9:39 AM, Duncan Murdoch wrote:
Whoops, as Martin Maechler pointed out to me, this line is unnecessary. 
  The fact that test is an argument to wrapper means that a local copy 
would have been made already.

(I am fairly sure this line wouldn't cost very much, since R would 
recognize that a third copy of test isn't needed, but I shouldn't have 
put it there.)

Duncan Murdoch