A function within a function in the R language

Viewed 112

I have a simple function in the R language, for instance:

f<-function(x){
y=(x+2)/3
return(y)
}

I want to evaluate this function five times on its own outputs, i.e., y1=f(x), y2=f(f(x)), y3=f(f(f(x))), y4=f(f(f(f(x)))), y5=f(f(f(f(f(x))))). Is there a simpler way to do it in the form of one function, for instance the function with two arguments when the first argument is x and the second argument is the number of evaluation times, i.e., n. For instance, for x=3 and n=5 I would like to have the function f2<-(x=3,n=5) whose output would be in the form of a vector or a list of the length equal to n with the values:

y1=f(3)=1.666667
y2=f(f(3))=1.222222
y3=f(f(f(3)))=1.074074
y4=f(f(f(f(3))))=1.024691
y5=f(f(f(f(f(3)))))=1.00823

How to write such one function in the R ?

4 Answers

Using the accepted answer in another post, just use Reduce to create the composition, g.

composite <- function(g, h) {function(...)g(h(...))}
g <- Reduce(composite, list(f, f, f, f, f))

g(2)
f(f(f(f(f(2)))))

identical(g(2), f(f(f(f(f(2))))))
#[1] TRUE

You can write a helper function like:

iterate <- function(f,s,n){
  iterates <- vector(length = n+1)
  iterates[1] <- s
  for(i in 1:n){
    iterates[i+1] <- f(iterates[i])
  }
  iterates
}

Then, for example,

> iterate(function(x){(x+2)/3},3,5)
[1] 3.000000 1.666667 1.222222 1.074074 1.024691 1.008230

You can of course modify the above code so that the vector would start with f(s) rather than s.

You can also tweak your original function like below:

f2<-function(x, steps){
             ans<-c(x)
              for (i in 1:steps) {
                   y=(x+2)/3
                   ans=append(ans,y)
                   x = y
  }
    return(ans)
}

f2(3,5)
#> [1] 3.000000 1.666667 1.222222 1.074074 1.024691 1.008230

Reduce will do this if you add a "junk" argument to your f. If you just want the last result set accumulate = F or just remove the accumulate argument from the function

f<-function(x, junk){
y=(x+2)/3
return(y)
}

Reduce(f, x = numeric(5), init = 3, accumulate = T)
# [1] 3.000000 1.666667 1.222222 1.074074 1.024691 1.008230

Or with purrr

purrr::accumulate(numeric(5), f, .init = 3)
# [1] 3.000000 1.666667 1.222222 1.074074 1.024691 1.008230
Related