R: passing expression to an inner function

匿名 (未验证) 提交于 2019-12-03 08:35:02

问题:

Further delving into the mysteries of R evaluation...This is closely related to my previous question ( How to write an R function that evaluates an expression within a data-frame ). Let's say I want to write a function topfn that takes a data-frame and an expression involving column-names of that data-frame. I want to pass both these arguments on to another function fn that actually evaluates the expression within the "environment" of the data-frame. And I want both fn and topfn to work correctly when passed a data-frame and an expression

My first attempt, as suggested in the answer to the above question, is to define:

 fn <- function(dfr, expr) {    mf <- match.call()    eval( mf$expr, envir = dfr )  } 

And define topfn like this:

topfn <- function(df, ex) {   mf <- match.call()   fn(df, mf$ex)  } 

Now if I have a data-frame

df <- data.frame( a = 1:5, b = 1:5 ) 

the inner function fn works fine:

> fn(df,a) [1] 1 2 3 4 5 

But the topfn does not work:

> topfn(df,a) mf$ex 

To fix this I first check the class of topfn(df,a),

> class(topfn(df,a)) [1] "call" 

This gives me an idea for an ugly hack to re-define fn as follows:

fn <- function(dfr, expr) {   mf <- match.call()   res <- eval(mf$expr, envir = dfr)     if(class(res) == 'call')     eval(expr, envir = dfr) else   res } 

And now both functions work:

> fn(df,a) [1] 1 2 3 4 5 > topfn(df,a) [1] 1 2 3 4 5 

As I said, this looks like an ugly hack. Is there a better way (or more standard idiom) to get these working? I've consulted Lumley's curiously-named Standard NonStandard Evaluation Rules document http://developer.r-project.org/nonstandard-eval.pdf but wasn't particularly enlightened after reading it. Also helpful would be any pointers to source-code of functions I can look at for examples.

回答1:

This is most easily avoided by passing strings into topfn instead of expressions.

topfn <- function(df, ex_txt)  {   fn(df, ex_txt)  }  fn <- function(dfr, expr_txt)  {            eval(parse(text = expr_txt), dfr)  }  df <- data.frame(a = 1:5, b = 1:5 ) fn(df, "a")                               fn(df, "2 * a + b") topfn(df, "a")              topfn(df, "2 * a + b") 

EDIT:

You could let the user pass expressions in, but use strings underneath for your convenience.

Change topfn to

topfn <- function(df, ex)  {   ex_txt <- deparse(substitute(ex))   fn(df, ex_txt)  } topfn(df, a)              topfn(df, 2 * a + b) 

ANOTHER EDIT:

This seems to work:

topfn <- function(df, ex)  {   eval(substitute(fn(df, ex))) }  fn <- function(dfr, expr)  {            eval(substitute(expr), dfr)  } fn(df, a)                               fn(df, 2 * a + b) topfn(df, a)              topfn(df, 2 * a + b) 


回答2:

You can use three dots to gather arguments and pass them to another function, is that what you mean?

ftop=function(...) f(...) f=function(a,b) a[b]  a=data.frame(b=10)  ftop(a,"b")  f(a,"b") 


标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!