2010-02-09 11 views
35

posso creare un operatore di composizione in R:funzioni di livello superiore in R - c'è una funzione di comporre operatore o curry?

`%c%` = function(x,y)function(...)x(y(...)) 

Per essere utilizzato in questo modo:

> numericNull = is.null %c% numeric 
> numericNull(myVec) 
[2] TRUE FALSE 

ma mi piacerebbe sapere se c'è un set ufficiale di funzioni per fare questo tipo di cosa e altre operazioni come il curry in R. In gran parte questo è per ridurre il numero di parentesi, le parole chiave di funzione ecc nel mio codice.

La mia funzione curry:

> curry=function(...){ 
    z1=z0=substitute(...);z1[1]=call("list"); 
    function(...){do.call(as.character(z0[[1]]), 
          as.list(c(eval(z1),list(...))))}} 
> p = curry(paste(collapse="")) 
> p(letters[1:10]) 
[1] "abcdefghij" 

Ciò è particolarmente piacevole per esempio aggregata:

> df = data.frame(l=sample(1:3,10,rep=TRUE), t=letters[1:10]) 
> aggregate(df$t,df["l"],curry(paste(collapse="")) %c% toupper) 
    l x 
1 1 ADG 
2 2 BCH 
3 3 EFIJ 

Il che sembra molto più elegante e modificabile a:

> aggregate(df$t, df["l"], function(x)paste(collapse="",toupper(x))) 
    l x 
1 1 ADG 
2 2 BCH 
3 3 EFIJ 

Fondamentalmente voglio sapere - è presente già stato fatto per la R?

+0

nota che la mia chiamata al curry non associa attualmente le variabili al curry dall'ambiente in cui è chiamata, ma da dove viene invocata la funzione restituita. Ci sto lavorando. –

+2

Se si desidera semplicemente una funzione Incolla con valori predefiniti migliori, perché preoccuparsi del curry? Basta definire, ad es. 'paste0 <- function (x, ...) paste (toupper (x), collapse =" ", ...)' Quindi puoi chiamare 'aggregate (df $ t, df [" l "], paste0)' –

+4

perché diventa più complesso quando si desidera impilare più funzioni. –

risposta

5

Il posto standard per la programmazione funzionale in R è ora la libreria functional.

Dalla libreria:

funzionale: curry, comporre, e altre funzioni di ordine superiore

Esempio:

library(functional) 
    newfunc <- Curry(oldfunc,x=5) 

CRAN: https://cran.r-project.org/web/packages/functional/index.html

PS : Questa libreria sostitutiva ute la libreria ROxigen.

2

C'è una funzione chiamata Curry nel pacchetto roxygen.
Trovato tramite this conversation sull'archivio di posta.

+0

Purtroppo il link non funziona. È probabilmente questo: https://stat.ethz.ch/pipermail/r-help/2009-December/221224.html –

+0

@FlorianJenn Grazie; Ho aggiornato il link nella risposta. –

28

Entrambe queste funzioni in realtà esistono in the roxygen package (see the source code here) da Peter Danenberg (è stato originariamente basata su Byron Ellis's solution on R-Help):

Curry <- function(FUN,...) { 
    .orig = list(...); 
    function(...) do.call(FUN,c(.orig,list(...))) 
} 

Compose <- function(...) { 
    fs <- list(...) 
    function(...) Reduce(function(x, f) f(x), 
         fs, 
         ...) 
} 

Nota l'uso della funzione Reduce, che può essere molto utile quando si cerca di fare programmazione funzionale in R. Vedi? Riduci per maggiori dettagli (che copre anche altre funzioni come Map e Filter).

e il suo esempio di Curry (leggermente diverso in questo uso):

> library(roxygen) 
> p <- Curry(paste, collapse="") 
> p(letters[1:10]) 
[1] "abcdefghij" 

Ecco un esempio per mostrare l'utilità di Compose (applicazione di tre funzioni diverse lettere):

> Compose(function(x) x[length(x):1], Curry(paste, collapse=""), toupper)(letters) 
[1] "ZYXWVUTSRQPONMLKJIHGFEDCBA" 

E il tuo esempio finale funzionerebbe in questo modo:

> aggregate(df[,"t"], df["l"], Compose(Curry(paste, collapse=""), toupper)) 
    l x 
1 1 ABG 
2 2 DEFH 
3 3 CIJ 

Infine, ecco un modo per fare la stessa cosa con plyr (potrebbe anche essere fatto facilmente con by o aggregate come già evidenziato):

> library(plyr) 
> ddply(df, .(l), function(df) paste(toupper(df[,"t"]), collapse="")) 
    l V1 
1 1 ABG 
2 2 DEFH 
3 3 CIJ 
+2

Non c'è qualcosa di sbagliato nell'usare un pacchetto per la programmazione alfabetica per la programmazione funzionale? Cosa dice sulla modularità delle librerie R? – piccolbo

+9

FWIW, 'Compose' e' Curry' sono stati spostati nel pacchetto 'functional' da un po 'di tempo fa. –

2

un approccio più complesso è necessario se si desidera che i 'nomi' delle variabili per passare attraverso accuratamente.

Ad esempio, se si esegue plot(rnorm(1000),rnorm(1000)), si ottengono belle etichette sugli assi xey. Un altro esempio di questo è data.frame

> data.frame(rnorm(5), rnorm(5), first=rpois(5,1), second=rbinom(5,1,0.5)) 
    rnorm.5. rnorm.5..1 first second 
1 0.1964190 -0.2949770  0  0 
2 0.4750665 0.8849750  1  0 
3 -0.7829424 0.4174636  2  0 
4 1.6551403 1.3547863  0  1 
5 1.4044107 -0.4216046  0  0 

Non che il data.frame ha assegnato i nomi utili alle colonne.

Alcune implementazioni di Curry potrebbero non funzionare correttamente, causando nomi di colonne e etichette di stampa non leggibili. Invece, io uso qualcosa di simile:

Curry <- function(FUN, ...) { 
    .orig = match.call() 
    .orig[[1]] <- NULL # Remove first item, which matches FUN 
    .orig[[1]] <- NULL # Remove another item, which matches Curried argument 
    function(...) { 
     .inner = match.call() 
     .inner[[1]] <- NULL # Remove first item, which matches Curry 
     do.call(FUN, c(.orig, .inner), envir=parent.frame()) 
    } 
} 

Questo è abbastanza complesso, ma penso che sia corretto. match.call catturerà tutti gli argomenti, ricordando completamente quali espressioni hanno definito gli argomenti (questo è necessario per le belle etichette). Il problema è che cattura troppi argomenti - non solo il ... ma anche lo FUN. Ricorda anche il nome della funzione chiamata (Curry).

Pertanto, vogliamo eliminare queste prime due voci in .orig in modo che .orig corrisponda davvero agli argomenti .... Ecco perché facciamo .orig[[1]]<-NULL due volte - ogni volta cancella una voce e sposta tutto il resto a sinistra.

Questo completa la definizione e ora siamo in grado di effettuare le seguenti operazioni per ottenere esattamente lo stesso come sopra

Curry(data.frame, rnorm(5), rnorm(5))(first=rpois(5,1) , second=rbinom(5,1,0.5)) 

Una nota finale sulla envir=parent.frame(). L'ho usato per garantire che non ci sarebbe un problema se si hanno variabili esterne chiamate ".inner" o ".orig". Ora, tutte le variabili vengono valutate nel punto in cui viene chiamato il curry.

+0

(Answerer here) L'ho appena inventato oggi. Suppongo che sia già stato fatto? E forse c'è un difetto che non conosco ?! –

Problemi correlati