do.call and curve can not plot a function inside another function environment
Asked Answered
M

4

2

I am facing a strange problem about do.call and curve:

func1 <- function (m, n) {
  charac <- paste ("func2 <- function(x)", m, "*x^", n, sep = "")
  eval(parse(text = charac))
  return(func2)
}
func3 <- function (m, n) {
  my.func <- func1 (m, n)
  do.call("curve",list(expr = substitute(my.func)))
}

func1 constructs func2 and func3 plots the constructed func2. But when I run func3, following error would be displayed:

> func3 (3, 6)
Error in curve(expr = function (x)  : 
  'expr' must be a function, or a call or an expression containing 'x'

However, while I run func1 and plot the output manually (without applying func3), func2 would be plotted:

my.func <- func1 (3, 6)
do.call("curve",list(expr = substitute(my.func)))

What happened here leads me to a confusion and I do not know why do.call can not plot func2 inside func3 local environment.

Thank you

Millsaps answered 12/1, 2013 at 9:59 Comment(0)
C
1

It is not a problem of do.call, but substitute which evaluate by default in the global environment. So you need to tell it in which environment substitution must occur. Here obviously in the local envir of func3.

This should work:

 do.call("curve",list(expr = substitute(my.func,
                                           env = parent.frame())))

Edit thanks Dwin

As said in the comment substitute env Defaults to the current evaluation environment. So Why the code below works? The answer in the help of substitute

formal argument to a function or explicitly created using delayedAssign(), the expression slot of the promise replaces the symbol. If it is an ordinary variable, its value is substituted, unless env is .GlobalEnv in which case the symbol is left unchanged.

env = parent.frame(n=1) is equivalent to .GlobalEnv, that why the symbol (my.func) is left unchanged. So the correct answer would be :

do.call("curve",list(expr = substitute(my.func,
                                               env = .GlobalEnv)))

To test , I open new R session :

func1 <- function (m, n) {
  charac <- paste ("func2 <- function(x)", m, "*x^", n, sep = "")
  eval(parse(text = charac))
  return(func2)
}
func3 <- function (m, n) {
  my.func <- func1 (m, n)

  do.call("curve",list(expr = substitute(my.func,env = .GlobalEnv)))
}

Than I call

 func3(2,6)
Cache answered 12/1, 2013 at 10:53 Comment(7)
@EhsanMasoudi if it works and you are satisfied of the question you can accept it by checking the box at left.Cache
The help page for substitute says it is evaluated in the current environment.Seam
Can you please create a context where this code doesn't throw an error? I get "could not find function 'my.curve"Seam
Not with the new code, but when the first argument to do.call is "my.curve" it provokes an error.Seam
@DWin oops...corrected! I hacked curve function and I forget to remove the name. thank's for your help.Cache
Or just use a closure: func2 <- function(x) x * m ^ nMeares
substitute doesn't evaluate anythingMeares
M
3

You are making this overcomplicated - you don't need to do anything special when creating f2:

f1 <- function (m, n) {
  function(x) m * x ^ n
}
f3 <- function (m, n) {
  f2 <- f1(m, n)
  curve(f2)
}
f3(3, 6)

This could, of course, be made more concise by eliminating f1:

f4 <- function (m, n) {
  f2 <- function(x) m * x ^ n
  curve(f2)
}
f4(3, 6)

You can find more information about R's scoping rules (which makes this work) at https://github.com/hadley/devtools/wiki/Functions

Meares answered 12/1, 2013 at 17:24 Comment(4)
++ :(more for the link than for the answer) I thought the question was how to pass arguments to functions that take expression arguments using do.call inside another function, but maybe I was over-reading it. Reading you material I wonder if the problem might be that what was being passed was a closure rather than a function?Seam
@DWin all functions are closures, so that's probably not the problem. But I don't really understand what you're trying to do in your answer. You might also find github.com/hadley/devtools/wiki/Computing-on-the-language helpful, but it's still pretty rough. github.com/hadley/pryr/blob/master/R/draw-tree.r is also useful.Meares
@Meares Yes I made it complicated, but it was just an simplified example of my main code. as you know R has no ability to handle symbolic computation (however there is a package called Ryacas and it's an interface to yacas). In my code such this method is applied to auto-construct Fisher information matrix for nonlinear models without user interfereMillsaps
@EhsanMasoudi R does have extensive capabilities for symbolic computation.Meares
C
1

It is not a problem of do.call, but substitute which evaluate by default in the global environment. So you need to tell it in which environment substitution must occur. Here obviously in the local envir of func3.

This should work:

 do.call("curve",list(expr = substitute(my.func,
                                           env = parent.frame())))

Edit thanks Dwin

As said in the comment substitute env Defaults to the current evaluation environment. So Why the code below works? The answer in the help of substitute

formal argument to a function or explicitly created using delayedAssign(), the expression slot of the promise replaces the symbol. If it is an ordinary variable, its value is substituted, unless env is .GlobalEnv in which case the symbol is left unchanged.

env = parent.frame(n=1) is equivalent to .GlobalEnv, that why the symbol (my.func) is left unchanged. So the correct answer would be :

do.call("curve",list(expr = substitute(my.func,
                                               env = .GlobalEnv)))

To test , I open new R session :

func1 <- function (m, n) {
  charac <- paste ("func2 <- function(x)", m, "*x^", n, sep = "")
  eval(parse(text = charac))
  return(func2)
}
func3 <- function (m, n) {
  my.func <- func1 (m, n)

  do.call("curve",list(expr = substitute(my.func,env = .GlobalEnv)))
}

Than I call

 func3(2,6)
Cache answered 12/1, 2013 at 10:53 Comment(7)
@EhsanMasoudi if it works and you are satisfied of the question you can accept it by checking the box at left.Cache
The help page for substitute says it is evaluated in the current environment.Seam
Can you please create a context where this code doesn't throw an error? I get "could not find function 'my.curve"Seam
Not with the new code, but when the first argument to do.call is "my.curve" it provokes an error.Seam
@DWin oops...corrected! I hacked curve function and I forget to remove the name. thank's for your help.Cache
Or just use a closure: func2 <- function(x) x * m ^ nMeares
substitute doesn't evaluate anythingMeares
S
1

This works:

func3 <- function (m, n) {
   my.func <- func1 (m, n); print(str(my.func))
   do.call(curve, list(expr=bquote( my.func) ) )
 }
Seam answered 12/1, 2013 at 11:26 Comment(4)
Can't tell why there's output with this method, though.Orly
curve is supposed to accept a function, so I don't understand your lack of understanding.Seam
sorry, I'm not talking about the plot, I'm talking about the jibberish that prints to screen.Orly
Sorry. That print() call was just my way of making sure that my.func was really a function at that point. It can safely be ignored or removed.Seam
P
-2

You just need to remove line:

my.func <- func1 (m, n)

from func3.

Phosphine answered 12/1, 2013 at 14:45 Comment(1)
How can that possibly help?Iolaiolande

© 2022 - 2024 — McMap. All rights reserved.