[Rd] Creating functions programmatically

Hadley Wickham h.wickham at gmail.com
Thu Oct 4 00:55:17 CEST 2012


> I think `function` does not eval its arguments, and it demands a
> pairlist. So this works:
>
> f <- eval(substitute(`function`(args, body),
> list(args=as.pairlist(alist(a=1)), body=quote(a+1))))
>
> The other thing to notice is a syntax difference between function and
> ordinary calls: when writing

Thanks.

So as far as I can tell, we have 4 basic ways of making a function
from args, body and env:

    make_function1 <- function(args, body, env = parent.frame()) {
      args <- as.pairlist(args)
      eval(call("function", args, body), env)
    }
    make_function2 <- function(args, body, env = parent.frame()) {
      f <- function() {}
      formals(f) <- args
      body(f) <- body
      environment(f) <- env

      f
    }
    make_function3 <- function(args, body, env = parent.frame()) {
      as.function(c(args, body), env)
    }
    make_function4 <- function(args, body, env = parent.frame()) {
      subs <- list(args = as.pairlist(args), body = body)
      eval(substitute(`function`(args, body), subs), env)
    }

    args <- alist(a = 1, b = 2)
    body <- quote(a + b)
    make_function1(args, body)
    make_function2(args, body)
    make_function3(args, body)
    make_function4(args, body)

And not that speed really matters much here, but:

    library(microbenchmark)
    microbenchmark(
      make_function1(args, body),
      make_function2(args, body),
      make_function3(args, body),
      make_function4(args, body),
      function(a = 1, b = 2) a + b
    )

Unit: nanoseconds
                          expr   min    lq median    uq    max
1 function(a = 1, b = 2) a + b   165   228    279   330    703
2   make_function1(args, body)  3540  4276   4774  5340  12947
3   make_function2(args, body) 47150 49170  49972 52510 147753
4   make_function3(args, body)  7461  8482   9116  9515  14763
5   make_function4(args, body)  4818  5814   6424  7102  13385

So constructing the call explicitly is fastest, followed by using
function, then as.function.  Modifying an empty function is an order
of magnitude slower. Even the fastest approach is about 20x slower
than creating it by hand.

Hadley

-- 
RStudio / Rice University
http://had.co.nz/



More information about the R-devel mailing list