5

For example, I have a vector of functions: fun_vec <- c(step1,step2,step3). Now I want to compose them like this: step1(step2(step3(x))). How do I do this using fun_vec? (Suppose that fun_vec isn't fixed and can have more or less functions.)

Jaap
  • 81,064
  • 34
  • 182
  • 193
Catiger3331
  • 611
  • 1
  • 6
  • 18

4 Answers4

5

Similar to Frank's use of freduce, you can use Reduce:

step1 <- function(a) a^2
step2 <- function(a) sum(a)
step3 <- function(a) sqrt(a)
steps <- list(step1, step2, step3)
Reduce(function(a,f) f(a), steps, 1:3)
# [1] 3.741657
step3(step2(step1(1:3)))
# [1] 3.741657

You can see it "in action" with:

Reduce(function(a,f) f(a), steps, 1:3, accumulate=TRUE)
# [[1]]
# [1] 1 2 3
# [[2]]
# [1] 1 4 9
# [[3]]
# [1] 14
# [[4]]
# [1] 3.741657
r2evans
  • 141,215
  • 6
  • 77
  • 149
4

You can use freduce from the magrittr package:

fun_vec = c(function(x) x^2, function(x) sum(x), function(x) sqrt(x))

library(magrittr)    
freduce(1:10, fun_vec)

Alternately, define a function sequence with pipes like...

library(magrittr)    
f = . %>% raise_to_power(2) %>% sum %>% sqrt

f(1:10)

A similar example: Is there a way to `pipe through a list'?

Frank
  • 66,179
  • 8
  • 96
  • 180
3

Here's a base R recursive approach:

compose <- function(funs) {
  n <- length(funs)
  fcomp <- function(x) funs[[n - 1]](funs[[n]](x))
  ifelse(n > 2, compose(c(funs[1:(n - 2)], fcomp)), fcomp)
}

x <- c(sqrt, log, exp)
compose(x)(2)
# [1] 1.414214
sqrt(log(exp(2)))
# [1] 1.414214

If the number of functions in funs is greater than two, we shorten the list by one by replacing the last two functions by their composition. Otherwise, we return the composition of the last remaining two. It's assumed that initially there are at least two functions in funs.

Julius Vainora
  • 47,421
  • 9
  • 90
  • 102
2

Take a look at purrr::compose. If your functions are stored inside a list, use purrr::invoke to pass that list to compose:

fun_vec <- c( exp, log10, sqrt )
f <- purrr::invoke( purrr::compose, fun_vec )
f(4)                      # 1.35125
exp( log10( sqrt(4) ) )   # 1.35125
Artem Sokolov
  • 13,196
  • 4
  • 43
  • 74