Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Function Factory in R

I try to come up with a function factory by returning a dictionary of specialized functions, more or less like functional programming style. I try to do this in the following code.

require(hash)

names = c("aa","bb","cc");
funs = hash()
for (i in seq(length(names))) {
  n = names[i]
  funs[[n]] = function(x) { 
    print(paste(n,":",x, sep="")) 
 }
}

Obviously, I have 3 functions in the array; however, they all behave the same as the last function in the iteration.

> funs[["aa"]](1)
[1] "cc:1"
> funs[["bb"]](2)
[1] "cc:2"
> funs[["cc"]](3)
[1] "cc:3"

My guess is that R didn't create new function instance but reuses the same function object inside the for-loop.

I try the following in hope that R will create different function object,

  funs[[n]] = eval(parse(text="function(x) { print(paste(n,':',x, sep='')) }"))

but it works the same as the first one.

Do you have an idea how to create a generator that creates different function objects?

like image 681
user2949165 Avatar asked Nov 03 '13 04:11

user2949165


1 Answers

According to Hadley's Advanced R Programmin, Lexical scoping, the variable n in the body of functions funs[['aa']], funs[['bb']], and funs[['cc']] are the variable n in <environment: R_GlobalEnv>.

For example:

> funs[["aa"]](1)
[1] "cc:1"
> n <- "1234"
> funs[["aa"]]("1")
[1] "1234:1"

To do what you want, I'll write a function which returns a function:

funs.gen <- function(n) {
  force(n)
  function(x) {
    print(paste(n, ":", x, sep=""))
  }  
}

names = c("aa","bb","cc");
funs = hash()
for (i in seq(length(names))) {
  n = names[i]
  funs[[n]] = funs.gen(n)
}

funs[["aa"]](1)
funs[["bb"]](2)
funs[["cc"]](3)

Note that force is used to ask R not lazy evaluate expression n. If you remove it, then R will evaluate n out of the for loop which will produce the same result as your question.

like image 195
wush978 Avatar answered Oct 11 '22 15:10

wush978