2013-05-11 14 views
5

Sto provando a scrivere un programma che accetta un'espressione come input e restituisce una funzione con quell'espressione associata come corpo.acquisizione di un'espressione come corpo di una funzione in R

caller <- function (expr, params) { 

    Function <- function (params, body, env = parent.frame()) { 
     # returns a function 

    } 

    Function(params, body = expr) 
} 

func <- caller (a + b, c('a', 'b')) 

func(1, 2) 
[1] 3 

posso associare i parametri abbastanza facilmente, usando qualcosa come

params <- c('a', 'b') 
f <- function() {} 
formals(f) <- structure(
    replicate(length(params), NULL), 
    names = params 
) 

sto avendo problemi a venire con un modo di aggiungere in modo dinamico l'espressione come il corpo. Ho provato a usare sostituto() e adattare make_function dalla libreria di pryr, ma non riesco a far funzionare le cose. Il mio miglior tentativo è

body(f, parent.frame()) <- as.list(match.call())[-1]$body 

Non ho potuto farlo funzionare anche con il sostituto. Qualche idea su come legare il corpo in modo che il programma più in alto funzioni come previsto?

Ho visto similar questions su SO, ma le soluzioni non sembrano soddisfare questo problema.

risposta

3

Ecco una soluzione per consentire i parametri senza valore predefinito. È anche più semplice passare i nomi dei parametri, in quanto non devono essere racchiusi tra virgolette.

Si prega di controllare i commenti nel codice qui sotto:

g <- function(...) 
{ 
    # Get the arguments as unevaluated expressions: 

    L <- as.list(substitute(list(...)))[-1] 

    # The first argument is the body expression (technically a call object): 

    expr <- L[[1]] 

    # If the expression is not enclosed in curly braces, let's force it: 

    if(as.character(expr[[1]]) != "{") expr <- call("{", expr) 

    # Drop the first argument: 

    L <- L[-1] 

    # Mark symbols to be used as names for missing parameters: 

    filter <- vapply(L, is.symbol, logical(1)) 

    params <- L 

    # The obscure expression "formals(function(x){})$x" returns a missing value, something really arcane ;-) : 

    params[filter] <- list(formals(function(x){})$x) 

    # Here the symbols are used as names: 

    names(params)[filter] <- vapply(L[filter], as.character, character(1)) 

    # Now the result: 

    f <- function(){} 

    formals(f) <- params 

    body(f) <- expr 

    # Just to make it nicier, let's define the enclosing environment as if the function were created outside g: 

    environment(f) <- parent.frame() 

    f 
} 

Alcuni test:

> g(a+b, a, b=1) 
function (a, b = 1) 
{ 
    a + b 
} 


> f <- g({x <- a+b; x^2}, a, b) 
> f 
function (a, b) 
{ 
    x <- a + b 
    x^2 
} 
> f(2,3) 
[1] 25 
> f(1) 
Error in a + b : 'b' is missing 

> g(a+b, a=2, b=2)() 
[1] 4 
+0

grazie :) Mi piace la tua soluzione per l'aggiunta di parametri senza un argomento predefinito – RyanGrannell

+0

Sembra che 'g (a + b, a, b = a)' non funzioni perché il tuo codice trasforma l'argomento della seconda funzione in semplicemente 'a' . Penso che sarebbe meglio non trasformare alcun argomento con nome. –

4

ne dite semplicemente:

caller <- function(expr, params) { 
    f <- function() NULL 
    formals(f) <- structure(replicate(length(params), NULL), names=params) 
    body(f, envir=parent.frame()) <- substitute(expr) 
    f 
} 

non utilizza una funzione interna, che potrebbe essere stato causando i problemi con substitute.

Nota che non sono sicuro se questo è l'impostazione dell'ambiente della funzione restituita nel modo desiderato. Questo lo imposta nell'ambiente dal quale si chiama caller.

+0

Non v'è alcun secondo argomento in 'body': Errore nel corpo (f, envir = parent.frame()): argomento non utilizzato (s) (envir = parent.frame()) –

+1

@ Ferdinand.kraft: il secondo argomento è solo nella forma di sostituzione di 'body', che è ciò che usa il' chiamante '. –

+0

Ho capito! Grazie! –

0

Un modo interessante alternativa di specificare gli argomenti della funzione è quello di utilizzare lo stesso meccanismo della funzione alist, che è comunemente usato in congiunzione con formals. Questo è come è definito nel pacchetto base:

alist <- function (...) as.list(sys.call())[-1L] 

Ciò è facilmente adattato per funzionare con caller:

caller <- function(...) { 
    f <- function() NULL 
    formals(f) <- as.list(sys.call())[-(1:2)] 
    body(f, envir=parent.frame()) <- substitute(list(...))[[2]] 
    f 
} 

Il primo argomento specifica ancora il corpo della funzione, ei rimanenti argomenti funziona esattamente come in alist.

> func <- caller(a+b, a=, b=10) 
> func(1) 
[1] 11 
> func <- caller(a+b, a=, b=a) 
> func(10) 
[1] 20 

È anche possibile creare funzioni che utilizzano ...:

> func <- caller(c(...), ...=) 
> func("a", "b", "c") 
[1] "a" "b" "c" 
Problemi correlati