Ben Bolker
Ben Bolker

Reputation: 226077

passing expressions to curve() within a function

A colleague asked me this, and I have struggled with it.

Suppose I want to define a function that takes an expression (let's say x^2 for concreteness) as an argument and passes that argument to curve().

If I want to do this the easy way, I just run

curve(x^2,from=0,to=3)

and it works fine.

Suppose I try to set up a wrapper function (let's say there are other things I want to do inside the wrapper in addition to plotting the curve):

f <- function(g) {
    curve(g,from=0,to=3)
}

This works if I pass a function:

f(function(x) x^2)

it fails if I try to pass x^2, when R tries to evaluate the expression:

f(x^2)
## Error in eval(expr, envir, enclos) (from #2) : object 'x' not found

I can try to forestall this by using substitute within the function:

f0 <- function(g) {
    str(substitute(g))
    curve(substitute(g),from=0,to=3)
}
f0(x^2)
## language x^2
## Error in curve(substitute(g), from = 0, to = 3) (from #3) : 
##  'expr' must be a function, or a call or an expression containing 'x'

OK, that suggests maybe I should try

f <- function(g) {
    h <- as.expression(substitute(g))
    str(h)
    curve(as.expression(substitute(g)),from=0,to=3)
}
f(x^2)
##  expression(x^2)
## Error in curve(as.expression(substitute(g)), from = 0, to = 3) (from #4) : 
##  'expr' must be a function, or a call or an expression containing 'x'

For what it's worth,

curve(expression(x^2),from=0,to=1)
## Error in curve(expression(x^2), from = 0, to = 1) : 
##   'expr' did not evaluate to an object of length 'n'

If I try debugging curve() to see what's happening, we have:

sexpr <- substitute(expr)
...
if (!((is.call(sexpr) || is.expression(sexpr)) && xname %in% 
    all.vars(sexpr))) 
    stop(...)

Here sexpr is substitute(g), which fails the xname %in% all.vars(sexpr) test ...

Any ideas about how to handle this?

Upvotes: 9

Views: 965

Answers (1)

Ben Bolker
Ben Bolker

Reputation: 226077

I didn't mean to ask-and-then-answer, but I figured out the (an?) answer with a little bit more poking around -- it occurred to me that I hadn't tried eval yet. eval(curve(substitute(g),from=0,to=3)) does not work, but this does:

f1 <- function(g) {
     eval(substitute(curve(g,from=0,to=3)))
}
f1(x^2)

update: @RichardScriven suggests that do.call() might work too -- and it does!

f2 <- function(g) {
    do.call(curve,list(substitute(g),from=0,to=3))
}
f2(x^2)

Further explication is welcome!

Upvotes: 6

Related Questions