Lisp/Scheme-like calls in R
Asked Answered
G

1

9

For some reasons, I'd like to play with R calls (at least as far as syntax is concerned) in a more Lisp/Scheme-like fashion (we all know that R has been heavily inspired by Scheme).

Thus, I set up the following function:

. <- function(f, ...)
   eval(match.call()[-1], envir=parent.frame())

Which allows me to express e.g. the following R code:

x <- sort(sample(1:10, 5, replace=TRUE))
for (i in x) {
   print(1:i)
}

in the following semantically equivalent form:

.(`<-`, x,
   .(sort, 
      .(sample,
         .(`:`, 1, 5),
         5, replace=TRUE)))

.(`for`, i, x,
   .(`{`, 
      .(print,
         .(`:`, 1, i))))

I'm quite satisfied with the current definition of . (as it's just made for fun). But it's surely far from perfect. In particular its performance is of course poor:

microbenchmark::microbenchmark(1:10, .(`:`, 1, 10))
## Unit: nanoseconds
##           expr  min      lq  median    uq   max neval
##           1:10  189   212.0   271.5   349   943   100
##  .(`:`, 1, 10) 8809 10134.5 10763.0 11467 44066   100

So I wonder if you could come up with some ideas concerning the definition of . that would address the above issue. C/C++ code is welcome.

Gaslight answered 20/6, 2014 at 14:53 Comment(2)
Two resources to look at that do related things: do.call and %>% from the magrittr package. The former is similar to your . function (but takes the subsequent arguments as a list, not a separate arguments). The latter does much with manipulation of calls to make complete function calls and may have some techniques for getting environments and efficiency right.Extortioner
@BrianDiggs thanks for the interesting link. I've seen some close votes, so I narrowed down my question only to performance issues.Gaslight
M
4

As Brian Diggs commented above, you can use do.call to perform faster calls without the overhead of eval.

> myfn <- function(f, ...)
+   do.call(f, list(...), envir=parent.frame())
> myfn(`:`, 1, 10)
 [1]  1  2  3  4  5  6  7  8  9 10

> microbenchmark::microbenchmark(1:10, .(`:`, 1, 10), myfn(`:`, 1, 10))
Unit: nanoseconds
             expr  min      lq  median      uq   max neval
             1:10  177   286.0   346.5   404.0   887   100
    .(`:`, 1, 10) 9794 11454.0 12141.5 12808.5 48391   100
 myfn(`:`, 1, 10) 3504  4413.5  4751.5  5287.5 48227   100

I suspect that getting equivalent performance to a bare function call will require modification of the R source itself.

Megan answered 24/7, 2014 at 15:5 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.