Area R: forced variable substitution in a function without a local environment

I define functions in a loop and try to force a loop variable to be evaluated without having to migrate a private environment.

Example: a set of functions handlers$h1 , handlers$h2 , ..., handlers$h6 , which simply go through 1, 2, ..., 6 to the following function:

 handlers <- list() for (i in 1:6) { handlers[[paste0('h', i)]] <- function () { message(i) # <-- example } } 

So handlers$h1() should be message 1, handlers$h2() should be message 2, ...

Instead, all functions return 6 , the current value of i .

To get around this, I can use closure as indicated in this question

 msg <- function(i) { force(i) function () { message(i) } } for (i in 1:6) { handlers[[paste0('h', i)]] <- msg(i) } 

Now each function works as expected, but each function should carry around its own environment:

 handlers$h1 # function () { message(i) } # <environment: 0x9342b80> 

How can I make handlers$h1 print function () { message(1) } , i.e. evaluated i and substituted it directly into the definition, eliminating the need for an environment?

The only ways I can think of are:

  • use eval (something that I prefer not to do);
  • write down each definition manually with the replacement 1-6 directly (excellent in this case, when there are only 6 functions, but, generally speaking, are not scalable)
+4
source share
3 answers

Here are some approaches that use body<-

You can use bquote

 handlers <- list() for (i in 1:6) { handlers[[paste0('h', i)]] <- function () {} body( handlers[[paste0('h', i)]]) <- bquote(message(.(i))) } handlers$h1 ## function () ## message(1L) 

or substitute

 for (i in 1:6) { handlers[[paste0('h', i)]] <- function () {} body( handlers[[paste0('h', i)]]) <- substitute(message(i), list(i=i)) } 
+6
source

Unfortunately, the R database lacks a function for executing functions manually, but pryr provides a make_function :

 library(pryr) handlers <- list() for (i in 1:6) { body <- substitute(message(i), list(i = i)) f <- make_function(alist(), body) handlers[[paste0('h', i)]] <- f } 

Note the use of substitute to manually modify the quoted call.

Another cool function (IMO!) In pryr is unenclose , which replaces the function by substituting into variables defined in the environment:

 msg <- function(i) { force(i) function () message(i) } msg(1) # function () message(i) # <environment: 0x102dc6ca0> unenclose(msg(1)) # function () # message(1) 

But there really is no shortage of using the initial closure.

+3
source

Here are two ways. They are the same, except for the line ## in each:

<strong> formals <-

 handlers <- list() f <- function() message(i) for (i in 1:6) { formals(f) <- list(i = i) ## handlers[[paste0('h', i)]] <- f } 

track

 handlers <- list() f <- function() message(i) for (i in 1:6) { trace(f, bquote(i <- .(i)), print = FALSE) ## handlers[[paste0('h', i)]] <- f } 
+1
source

All Articles