R:将表达式传递给内部函数

Posted

技术标签:

【中文标题】R:将表达式传递给内部函数【英文标题】:R: passing expression to an inner function 【发布时间】:2011-06-09 04:46:26 【问题描述】:

进一步深入研究 R 评估的奥秘...这与我之前的问题 (How to write an R function that evaluates an expression within a data-frame) 密切相关。假设我想编写一个函数topfn,它接受一个数据框和一个涉及该数据框列名的表达式。我想将这两个参数传递给另一个函数fn,该函数实际上评估数据框“环境”内的表达式。 我希望 fntopfn 在传递数据帧和表达式时都能正常工作

正如上述问题的答案所建议的,我的第一次尝试是定义:

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

并像这样定义topfn

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

如果我有一个数据框

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

内部函数fn 工作正常:

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

但是topfn 不起作用:

> topfn(df,a)
mf$ex

为了解决这个问题,我首先检查topfn(df,a) 的类,

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

这让我想到了一个丑陋的黑客重新定义 fn 如下:

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

现在这两个函数都可以工作了:

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

正如我所说,这看起来像一个丑陋的黑客。有没有更好的方法(或更标准的习语)来让这些工作? 我已经查阅了 Lumley 奇怪地命名为 Standard NonStandard Evaluation Rules 的文档 http://developer.r-project.org/nonstandard-eval.pdf,但在阅读后并没有特别开明。任何指向函数源代码的指针也很有帮助,我可以查看示例。

【问题讨论】:

Hadley Wickham 有一个很棒的关于 R 评估主题的 wiki:github.com/hadley/devtools/wiki/Evaluation 正如我建议的那样,您需要仔细区分交互式使用的函数和从其他函数调用的函数。调用任何使用替代和其他技巧的函数是非常困难的。换句话说,fntopfn 都没有干净的方法来处理相同类型的输入 - 这是一件好事。 @hadley 我明白你的意思:为交互式使用而设计的函数可以使用替代/其他技巧来轻松在控制台上键入它们(即没有引号等)。并且仅设计为由其他函数调用的函数不应使用此类技巧,因为它们可能会意外中断。特别是,@Richie 在他的回答结束时提供的解决方案,即使它在我上面的特定场景中有效——在其他场景中使用时它可能会中断(尤其是 fn )——我认为这就是你的你说对吧? 如果您能提供更多关于您想要实现的目标的详细信息,我将能够提供更具体的建议 - 很难在摘要中提供帮助。 两个选项:走 plyr/ggplot 路线并使用特殊函数引用。 .来自 plyr 可能会满足您的需求。否则,为每个函数编写一个程序员和交互式版本:交互式版本捕获表达式,然后调用。从长远来看,无论哪种方式都会为您节省很多痛苦。 【参考方案1】:

通过将字符串传递给topfn 而不是表达式,最容易避免这种情况。

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")

编辑:

您可以让用户传入表达式,但为方便起见,请在下面使用字符串。

topfn 更改为

topfn <- function(df, ex) 

  ex_txt <- deparse(substitute(ex))
  fn(df, ex_txt) 

topfn(df, a)             
topfn(df, 2 * a + b)

另一个编辑:

这似乎有效:

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)

【讨论】:

@richie:谢谢,是的,但我一直在寻找一种避免传递字符串的方法。我希望这些函数像 R 中的许多其他函数一样工作,这些函数将不带引号的表达式作为参数。 查看我对上一个问题的回答,了解fn 的第三个(格子样式)规范。 ***.com/questions/4682709/… @Richie 是的,我看到了,谢谢,这实际上是让它为内部函数工作的最简单的方法,但是将它适应上面的嵌套情况同样有问题。 @Richie 我看到了你的更新:topfn 现在接受一个表达式,但内部 fn 仍然接受一个字符串。我想两者都表达。上下文是我写了一个有用的函数fn,它接受一个表达式参数,我写了另一个有用的包装器topfn,它也接受一个表达式arg,我想把它传递给fn @Prasad:希望这个版本没问题;过度使用evalsubstitute 让我头疼。【参考方案2】:

您可以使用三个点来收集参数并将它们传递给另一个函数,这是您的意思吗?

ftop=function(...) f(...)
f=function(a,b) a[b]

a=data.frame(b=10)

ftop(a,"b")

f(a,"b")

【讨论】:

不完全。我不想使用点,我希望将显式参数传递给内部函数。

以上是关于R:将表达式传递给内部函数的主要内容,如果未能解决你的问题,请参考以下文章

如何将内部回调的结果传递给其父函数? [复制]

c++ - 如何将多维数组传递给没有内部维度的函数? [复制]

避免 nodeJs 中的回调地狱/将变量传递给内部函数

在 R 中,如何在绘图的两个内部函数之间传递数据帧/小标题,然后保存绘图?

将“继续”从函数内部传递到 Python 中的外部循环

将 $@ 传递给 shell 脚本中的函数