A recursive compose function in R?

  • A+
Category:Languages

I would like to create a function, "compose" in R which will compose an arbitrary number of functions given as arguments.

So far, I have accomplished this by defining a function "of" that composes two arguments and then Reducing this:

of <- function(f,g) function(x) f(g(x)) id <- function(x) x  compose <- function(...) {   argms = c(...)   Reduce(of,argms,id) } 

This seems to work fine, but since I'm learning R, I thought I'd try to write it in an explicit recursive style, i.e. forgoing the use of Reduce, iow the sort of thing that you would do in Scheme like this:

(define (compose . args)   (if (null? args) identity       ((car args) (apply compose (cdr args))))) 

I have come up against a number of obstacles, the major one at the moment seems to be that the first element of the arguments is not getting recognized as a function. My weak attempt so far:

comp <- function(...) {   argms <- list(...)   len <- length(argms)   if(len==0) { return(id) }   else {     (argms[1])(do.call(comp,argms[2:len]))    } } 

Spits out: Error in comp(sin, cos, tan) : attempt to apply non-function

There must be some elegant way to do this which eludes me. Any suggestions?

 


1) Try this:

comp1 <- function(f, ...) {   if (missing(f)) identity   else function(x) f(comp1(...)(x)) }   # test  comp1(sin, cos, tan)(pi/4) ## [1] 0.5143953  # compose is defined in the question compose(sin, cos, tan)(pi/4) ## [1] 0.5143953  functional::Compose(tan, cos, sin)(pi/4) ## [1] 0.5143953  sin(cos(tan(pi/4))) ## [1] 0.5143953  library(magrittr) (pi/4) %>% tan %>% cos %>% sin ## [1] 0.5143953  (. %>% tan %>% cos %>% sin)(pi/4) ## [1] 0.5143953 

1a) A variation of (1) which uses Recall is:

comp1a <- function(f, ...) {   if (missing(f)) identity   else {     fun <- Recall(...)     function(x) f(fun(x))   } }  comp1a(sin, cos, tan)(pi/4) ## [1] 0.5143953 

2) Here is another implementation:

comp2 <- function(f, g, ...) {   if (missing(f)) identity   else if (missing(g)) f   else Recall(function(x) f(g(x)), ...) }  comp2(sin, cos, tan)(pi/4) ## [1] 0.5143953 

3) This implementation is closer to the code in the question. It makes use of of defined in the question:

comp3 <- function(...) {   if(...length() == 0) identity   else of(..1, do.call("comp3", list(...)[-1])) } comp3(sin, cos, tan)(pi/4) ## [1] 0.5143953 

Comment

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen: