0

How to use with() (or alternative) in a function such that the data is updated and returned after executing the function? I know that the data (in my case the objects in the list I want to update in each time-step and input parameters to do this) are being called, commands in the for-loop are being executed but the updataded data is not being returned. Why is this the case? Am I missing something regarding the with() function?

I have also tried within() and within.list() which returns the same output which is this list at initialisation.

#~ input parameters
xPRM <- list()
xPRM$n_ts   <- 4
xPRM$n_yrs  <- 1
xPRM$n_x1   <- 25
xPRM$n_x2   <- 10
xPRM$n_x3   <- 5

set.seed(1)
my_func <- function(X){
        #~ lists to collect data after functions have been executed in each time-step
        list1    <- vector(mode = "list", length = X$n_ts*X$n_yrs)
        list2    <- vector(mode = "list", length = X$n_ts*X$n_yrs)
        # etc.
        
        #~ list for compartments (computations in each time step will be in these compartments)
        comp.1    <- list()
        comp.2    <- list()
        # etc. 
        
        #~ comp.1 initialisation
        comp.1$ts <- rep(0, X$n_x1)                                      
        comp.1$yr <- rep(0, X$n_x1)
        comp.1$v1 <- rnorm(X$n_x1, X$n_x2, X$n_x3)        
        comp.1$v2 <- rep(0, X$n_x1)
        
        with(c(comp.1, X), 
                    for(i in 1:n_ts*n_yrs){
                            ts <- i
                            yr <- ceiling(i/n_ts)
                            
                            v1[v1 >= 5]  <- v1[v1 >= 5]^(n_x3/i)    #~ do something 
                            v2[v1 >= 10] <- v2[v1 >= 10] + n_x3*i   #~ do something again 
                            #print(v2)                              #~ will show that the somethings are being done


                            #~Return data at end of time step
                            list1[[i]] <- comp.1
                            # list2[[i]] <- comp.2
                            # etc.
                    }
        )
        all_lists <- list(A = list1, B = list2
                          # etc.,
        )
        all_lists
        # list1
        # comp.1
}

my_func(X = xPRM)

EDIT

An example of the expected output can be produced with the following code:

set.seed(1)
#~ lists to collect data after functions have been executed in each time-step
list1    <- vector(mode = "list", length = X$n_ts*X$n_yrs)
list2    <- vector(mode = "list", length = X$n_ts*X$n_yrs)
# etc.

#~ comp.1 initialisation
comp.1$ts <- rep(0, xPRM$n_x1)                                      
comp.1$yr <- rep(0, xPRM$n_x1)
comp.1$v1 <- rnorm(xPRM$n_x1, xPRM$n_x2, xPRM$n_x3)        
comp.1$v2 <- rep(0, xPRM$n_x1)
for(i in 1:xPRM$n_ts*xPRM$n_yrs){
        comp.1$ts <- i
        comp.1$yr <- ceiling(i/xPRM$n_ts)
        
        comp.1$v1[comp.1$v1 >= 5]  <- comp.1$v1[comp.1$v1 >= 5]^(xPRM$n_x3/i)    #~ do something 
        comp.1$v2[comp.1$v1 >= 10] <- comp.1$v2[comp.1$v1 >= 10] + xPRM$n_x3*i   #~ do something again 
        
        #~Return data at end of time step
        list1[[i]] <- comp.1
        # list2[[i]] <- comp.2
        # etc.
}
all_lists <- list(A = list1, B = list2
                  # etc.,
)
#~ Expected output
all_lists
altfi_SU
  • 584
  • 1
  • 3
  • 15

1 Answers1

1

The problem is that with() performs all its operations, including assignment, in the local environment created when it is called. That means list1 isn't getting updated outside of the with() scope.

From the with() docs:

with is a generic function that evaluates expr in a local environment constructed from data.
...
Note that assignments within expr take place in the constructed environment and not in the user's workspace.
...
For interactive use this is very effective and nice to read. For programming however, i.e., in one's functions, more care is needed, and typically one should refrain from using with(), as, e.g., variables in data may accidentally override local variables, see the reference.

You can insist that list1 be updated in the global context with the <<- assignment operator. (Note that this kind of forced global assignment should be used with caution - see discussion here.)

In addition, comp.1 is not actually updated within its own with() context, so you'll need to build a list when you've finished your calculations and store that in list1[[i]].

Like this:

list1[[i]] <<- list(ts = ts, yr = yr, v1 = v1, v2 = v2)

Once these changes are made, the output from the with() implementation matches your expected output exactly.

andrew_reece
  • 20,390
  • 3
  • 33
  • 58
  • I see, thank you. Please, can you explain a little with regards to "(Note that this kind of forced global assignment should be used with caution, if at all.)"? Is in relation to the overriding of local variables as stated in the `with()` docs? – altfi_SU Sep 19 '20 at 21:00
  • It's not bad to use `<<-`, per se. It's just that you need to be careful when you're adjusting scopes outside of the one you're in. There's some discussion of it in [this post](https://stackoverflow.com/a/2629493/2799941). I added a link in the body of my answer. – andrew_reece Sep 19 '20 at 21:05
  • You're welcome. If this post answered your question, please mark it accepted by clicking the check to the left of the post. – andrew_reece Sep 19 '20 at 21:17