R: передача выражения во внутреннюю функцию

Далее вникаем в тайны оценки R... Это тесно связано с моим предыдущим вопросом (Как написать функцию R, которая оценивает выражение в кадре данных). Скажем, я хочу написать функцию topfn, которая принимает кадр данных и выражение с именами столбцов этого кадра данных. Я хочу передать оба этих аргумента другой функции fn, которая фактически оценивает выражение внутри "среды" кадра данных. И я хочу, чтобы как fn, так и topfn работали корректно, когда передавали кадр данных и выражение

Моя первая попытка, предложенная в ответе на вышеупомянутый вопрос, заключается в определении:

 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 document http://developer.r-project.org/nonstandard-eval.pdf, но не был особенно просвещен после его прочтения. Также полезно было бы указать указатели на исходный код функций, на которые я могу посмотреть на примерах.

Ответы

Ответ 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")

EDIT:

Вы можете позволить пользователю передавать выражения, но используйте для этого строки.

Измените 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)

Ответ 2

Вы можете использовать три точки для сбора аргументов и передачи их другой функции, это то, что вы имеете в виду?

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

a=data.frame(b=10)

ftop(a,"b")

f(a,"b")