views:

464

answers:

2

I'm trying to iteratively generate some functions using a For Loop:

# Create a list to hold the functions
funcs <- list()
funcs[]

# loop through to define functions
for(i in 1:21){

    # Make function name
    funcName <- paste( 'func', i, sep = '' )

    # make function
    func = function(x){x * i}

    funcs[[funcName]] = func

    }

However, it's not working as I hoped as the i value is not being evaluated within each function. I want to try and define the function to equal x * 1; x * 2; etc, but what I end up with is a function that is x * i; where i is 21.

I tried using the eval() function and that just resulted in x * eval(i) being stored.

+2  A: 

Check this one:

# Create a list to hold the functions
funcs <- list()
funcs[]

# loop through to define functions
for(i in 1:21){

    # Make function name
    funcName <- paste( 'func', i, sep = '' )

    # make function
    func = paste('function(x){x * ', i,'}',sep = '')

    funcs[[funcName]] = eval(parse(text=func))

    }
gd047
thank you - that worked!
celenius
+5  A: 

Use a closure (a function that write functions):

multiply <- function(i) {
  force(i)
  function(x) x * i
}

funcs <- list()
for(i in 1:21){
  funcName <- paste( 'func', i, sep = '' )
  funcs[[funcName]] = multiply(i)
}

# OR in one line, with lapply
funcs <- lapply(1:21, multiply)
hadley
My understanding of a closure (in the general sense) is that it's a function that carries bindings to variables that were in the lexical scope of its definition, at the *time* of its definition. In R, that doesn't seem to be the case, or else celenius' original example would have worked.Does R allow the creation of "true" closures somehow, perhaps by explicitly specifying the environment or something?
Ken Williams
Right, so every 'i' in each function was bound to the 'i' in the loop index. They were bound to the variable not the value. This is what you will get in any functional programming language.
Ian Fellows
Hadley's code doesn't work though. `funcs$func3(3)` should return 9, but it returns 63, as do all the functions in `funcs`.
Ken Williams
Ken, R functions have an environment, so you can (kind of) do closures. See my answer to this question using "local". Of course, you can alter the contents of the functions environment after the fact. E.g. 'assign("i", 3.141, environment(myfun))'. But be careful with that, because the environment for 'myfun' might be your global environment.
leif
leif, your explanation isn't quite right - it's the lazy evaluation that was preventing my code from working. By explicitly forcing the evaluation of i in the scope of the closure the code now works.
hadley
Hadley, you're right. In your original sample, R isn't evaluating the 'i' in multiply until the loop ends, but the 'force' makes it evaluate immediately. The documentation makes it look like the net effect is the same as using the 'local' statement if you are only concerned with one variable.
leif