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