R: передача выражения во внутреннюю функцию - PullRequest
14 голосов
/ 14 января 2011

Дальнейшее углубление в загадки оценки 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

Как я уже говорилэто выглядит как уродливый хак.Есть ли лучший способ (или более стандартная идиома), чтобы заставить их работать?Я проконсультировался с любопытно названным документом Стандартных нестандартных правил оценки Ламли http://developer.r -project.org / nonstandard-eval.pdf , но после его прочтения он мне не особо понравился.Также полезными могут быть любые указатели на исходный код функций, на которые я могу посмотреть для примеров.

Ответы [ 2 ]

13 голосов
/ 14 января 2011

Этого проще всего избежать, передавая строки в 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)
1 голос
/ 14 января 2011

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

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

a=data.frame(b=10)

ftop(a,"b")

f(a,"b")
...