2015-11-16 5 views
4

Я хочу построитьЭлегантный способ определить функцию внутри другой функции

f <- function(...) { 
    g <- function(x) x^2 
    list(...) 
} 

так, что я могу вызвать с помощью f(g(4)) и имеют list(...) результат в list(16).

В общем, я определю несколько временных функций внутри f, которые пользователь может вызвать при вызове f(...).

Я экспериментировал с assign и newenvironment, но только что стал более смущенным. Приветствуется помощь с элегантным решением.

Причина в том, что я хочу, чтобы функция в пакете Hmisc, drawPlot, позволяла пользователям указывать универсально названные функции в качестве входных данных для создания серии графических элементов, и я не хочу зарезервируйте эти имена общего типа. Например:

d <- drawPlot(Curve(), Points()) # interactively make a curve and 
            # a set of points 
+1

@TheTime Не должен быть 'list (eval (substitute (...)))'? – cryo111

+0

@TheTime Это может быть удобно для передачи аргументов, похожих на 'formula'. data.table имеет такую ​​псевдофункцию в 'melt.data.table' (называемой' pattern'); это даже не настоящая функция, просто ярлык для разбора аргументов (я думаю). – Frank

+0

'dplyr :: select' имеет аналогичную функциональность, приведенную в действие пакетом' lazyeval'. Вы можете получить вдохновение из [исходного кода] (https://github.com/hadley/dplyr/blob/master/R/select-vars.R) – shadowtalker

ответ

8

Я предполагаю, что вам на самом деле нужно что-то более сложное, чем это, но следующий делает то, что вы просили в вашем приводимый пример:

f <- function(...) { 
    g <- function(x) x^2 
    list(eval(substitute(...))) 
} 

f(g(4)) 
# [[1]] 
# [1] 16 

Или, если пользователи могут предоставить один или несколько вызовов функций, что-то вроде этого:

f <- function(...) { 
    g <- function(x) x^2 
    h <- function(x) 100*x 
    cc <- as.list(substitute(list(...))[-1]) 
    res <- list() 
    for(i in seq_along(cc)) { 
     res[[i]] <- eval(cc[[i]]) 
    } 
    res 
} 
f(g(4), h(5)) 
# [[1]] 
# [1] 16 
# 
# [[2]] 
# [1] 500 
3

Очень похоже на this answer, но я думаю, что, может быть, более расширяемой и ближе к оригинальной идее:

match.fun_wrapper <- function(...) { 
    # `match.fun` searches in the parent environment of the environment that 
    # calls `match.fun`, so this wrapper is a hack to be able to search in 
    # the current environment rather than the parent of the current environemnt 
    match.fun(...) 
} 

f <- function(fun, ...) { 
    g <- function(x) x^2 
    fun <- match.fun_wrapper(substitute(fun)) 
    fun(...) 
} 

Если вы хотите избавиться от match.fun, вы можете также избавиться от обертки хака:

f <- function(fun, ...) { 
    g <- function(x) x^2 
    fun(...) 
} 
0

Он смотрит на меня, как то, что вы пытаетесь сделать что-то вроде этого:

f <- function(fun, ...) { 
    g <- function(x) x^2 
    h <- function(x) x^3 
    i <- function(x) x^4 
    switch(fun, 
      'g' = g(...), 
      'h' = h(...), 
      'i' = i(...)) 
} 

> f('g', 3) 
[1] 9 
> f('h', 3) 
[1] 27 
> f('i', 3) 
[1] 81 

это не понятно, почему вы хотели бы, чтобы, если вы просто пытаетесь инкапсулировать функции с одинаковыми именами внутри разных пространств имен и использовать это как Hacky обходной путь за то R не предлагает неполносимметричных избранных классов. Если это так, вы также можете просто использовать фактические пространства имен, т. Е. Поместить свои функции внутри пакета, чтобы их вызывали по package::g(arg) вместо f('g', arg).

+0

Я добавил свои рассуждения к исходному вопросу, что делает не относятся к полнофункциональным классам. Поскольку я хочу, чтобы аргументы 'f' были числом вызовов функций, я не думаю, что ваш подход сделает трюк. –

+0

Вы правы. Мне кажется, что ответ Джоша О'Брайена лучше всего. Я не видел этого, когда писал, и добавил это, потому что ssdecontrol не работает, возвращая ошибку, если я попытаюсь запустить ее. –

Смежные вопросы