[R] turning R expressions into functions?

Thomas Lumley tlumley at uw.edu
Sat Jun 30 03:22:52 CEST 2012


]On Sat, Jun 30, 2012 at 2:36 AM, Jochen Voß <voss at seehuhn.de> wrote:
> [ please copy me on answers, since I am not subscribed to the list ]
>
> Dear all,
>
> I am trying to write an R function which uses system.time
> to determine which of a given list of R expressions executes
> fastest.  To work around the limited resolution of system.time,
> I want to convert the given expressions into functions which
> execute the given expressions a fixed number of times.
> My current attempt is as follows:
>
>  FuncIt <- function(k, expr) {
>   k <- as.numeric(k)
>   expr <- eval.parent(substitute(expr))
>   eval(substitute(function() { for (funcit.i in 1:k) { expr } }))
>  }
>
> This works, but seems not very robust.
> My question: is there a better way of doing this?
>
> Here are some experiments.
>
> 1) good: If I run the following using "Rscript"
>
>  test1 <- function(e1) {
>   e1 <- substitute(e1)
>   FuncIt(100, e1)
>  }
>
>  f <- test1(rnorm(1))
>  print(f)
>
> then I get the following output:
>
>  function ()
>  {
>     for (funcit.i in 1:100) {
>         rnorm(1)
>     }
>  }
>  <environment: 0x102260c28>
>
> This is what I want.  But why do I need the extra "substitute"
> in test1?  I only found by experiment that this is needed.

You don't.  You need an extra quote() in the argument.

That is, rnorm(1) is a call to the rnorm() function.  Since R passes
by value, the formal argument e1 is evaluated, returning a number, and
that is what ends up in your code.

If you want to pass a piece of unevaluated code to a function you
should ideally use quote() or expression() to wrap it, so that it is
not evaluated.

You can get around this using substitute(), which extracts the
unevaluated code from the formal argument, but it's probably a bad
idea, since the user of the function should expect all the arguments
to be evaluated.


>
> 2) bad: If I try to call FuncIt directly, it fails:
>
>  f <- FuncIt(100, rnorm(1))
>  print(f)
>
> has the output:
>
>  function ()
>  {
>     for (funcit.i in 1:100) {
>         -0.763894772833099
>     }
>  }
>  <environment: 0x102265790>
>
> This is bad, since now 'rnorm(1)' already has been
> evaluated.  How do I prevent this from happening,
> without breaking the good case 1 above?
>

Use quote() or expression() rather than trying to trick the evaluator.


> 3) ugly: If I run the same commands in the R gui on MacOS
> (R 2.15.1 released on 2012/06/22), I get different output:
>
>> source("/Users/voss/project/statcomp/test.R")
>  function() { for (funcit.i in 1:k) { expr } }
>  <environment: 0x19cc040>
>  function() { for (funcit.i in 1:k) { expr } }
>  <environment: 0x19bc884>
>
> This is on the same machine using (as far as I can tell) the
> same R engine.  So why is the output different?

The "ugly" formatting is how you formatted the code in the call to
eval(), and the good formatting is how R would print it without any
source constraints, so this is probably something to do with the
keep.source= argument source().

   -thomas

-- 
Thomas Lumley
Professor of Biostatistics
University of Auckland



More information about the R-help mailing list