funzioni di livello superiore in R - Esiste una funzione ufficiale operatore di comporre o curry?
-
19-09-2019 - |
Domanda
posso creare un operatore di composizione in R:
`%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 genere di cose e di altre operazioni come currying in R. In gran parte si tratta di ridurre il numero di staffe, 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"
Questo è 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
In sostanza vorrei sapere -? È presente già stato fatto per R
Soluzione
Il luogo 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 sostituisce la libreria ROxigen
Altri suggerimenti
Entrambe queste funzioni esiste effettivamente in pacchetto roxygen
( vedere il codice sorgente qui ) da Peter Danenberg (originariamente sulla base di La soluzione di Byron Ellis su R-Aiuto ):
Curry <- function(FUN,...) {
.orig = list(...);
function(...) do.call(FUN,c(.orig,list(...)))
}
Compose <- function(...) {
fs <- list(...)
function(...) Reduce(function(x, f) f(x),
fs,
...)
}
Si noti l'utilizzo della funzione Reduce
, che può essere molto utile quando si cerca di fare programmazione funzionale in R. Vedi? Ridurre per maggiori dettagli (che copre anche altre funzioni come Map
e Filter
).
E il vostro 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 ultimo esempio dovrebbe funzionare 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 facilmente essere fatto 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
C'è una funzione chiamata Curry nel roxygen pacchetto.
Trovato tramite questa conversazione sulla R Mail Archive.
Un approccio più complesso è necessario se si desidera che i 'nomi' delle variabili di passare attraverso con precisione.
Ad esempio, se si plot(rnorm(1000),rnorm(1000))
allora si otterrà belle etichette sul vostro x ed assi y-. 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 non possono farlo correttamente, portando a nomi di colonna e le etichette illeggibili trama. Invece, io uso qualcosa di simile:
Curry <- function(FUN, ...) {
.orig = match.call()
.orig[[1]] <- NULL # Remove first item, which matches Curry
.orig[[1]] <- NULL # Remove another item, which matches FUN
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
prenderà tutte args, completamente ricordando ciò che espressioni definite le args (ciò è necessario per belle etichette). Il problema è che si prende troppe args - non solo la ...
ma anche il FUN
. Si ricorda anche il nome della funzione che viene chiamata (Curry
).
Pertanto, vogliamo eliminare queste prime due voci in modo che .orig
.orig
realtà solo corrisponde agli argomenti ...
. Ecco perché facciamo .orig[[1]]<-NULL
due volte -. Ogni volta che elimina una voce e sposta tutto il resto a sinistra
Questo completa la definizione e ora possiamo procedere come segue 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()
. Ho usato questo per garantire che non ci sarà un problema se si dispone di variabili esterne chiamate '.inner' o '.orig'. Ora, tutte le variabili vengono valutate nel luogo in cui il curry è chiamato.