user3666707
user3666707

Reputation: 33

Variable number of for loops in R

I'm working in R and would like to pass a vector to a function. The vector gives the maximum values for a series of for loops. If the vector is (3,4,6,5), then the following code should be run. So the number of for loops depends on the length of the vector that is passed to the function. Then for each possibility, the counters are input into another function fn. Provided an example for a possible fn below.

S=0
fn = function(x){sum(x^2)}

for (i in 0:3){
   for (j in 0:4){
      for (k in 0:6){
         for (l in 0:5){
            S=S+fn(c(i,j,k,l))
       }
     }
   }
 }

I believe recursion is the way to go here, but I haven't had any luck figuring it out, and most of the recursion examples I've seen seem to be at a very high or very low level.

Any idea what the best way to approach this problem is?

Upvotes: 3

Views: 1595

Answers (3)

rob
rob

Reputation: 47

@Dason's answer is very nice but fails when all elements of vec are equal (because mapply then converts the result from a list to a matrix, whereas do.call requires a list). This can be rectified by adding SIMPLIFY = FALSE:

class(mapply(seq, 0, c(2, 2)))
[1] "matrix"
class(mapply(seq, 0, c(2, 2), SIMPLIFY = FALSE))
[1] "list"

The amended solution is then

f <- function(vec, fn){
  vecs <- mapply(seq, 0, vec, SIMPLIFY = FALSE)
  tmp <- do.call(expand.grid, vecs)
  tmp <- apply(tmp, 1, fn)
  sum(tmp)
}

fn = function(x){sum(x^2)}
f(c(3, 4, 6, 5), fn = fn)

Upvotes: 0

Greg Snow
Greg Snow

Reputation: 49640

I like @Dason's version using expand.grid, but here is a recursive version for cases when that approach may not work, or for general enlightenment:

recfun <- function(a,b) {
    S <- 0
    if(length(b)) {
        for( i in seq(0,b[1]) ) {
            S <- S + Recall( c(a,i), b[-1] )
        }
    } else {
        return(fn(a))
    }
    S
}

recfun( numeric(0), c(3,4,6,5) )

For practical use you may want to wrap this in another function that that takes the vector of interest and passes it to the function as b with the empty vector as a.

Upvotes: 1

Dason
Dason

Reputation: 61903

No need to use loops

f <- function(vec, fn){
  vecs <- mapply(seq, 0, vec)
  tmp <- do.call(expand.grid, vecs)
  tmp <- apply(tmp, 1, fn)
  sum(tmp)
}

fn = function(x){sum(x^2)}
f(c(3, 4, 6, 5), fn = fn)

Upvotes: 7

Related Questions