Funciones de nivel superior en R - ¿Existe un operador de composición oficial o una función de curry?

StackOverflow https://stackoverflow.com/questions/2228544

Pregunta

Puedo crear un operador de composición en R:

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

Para ser usado así:

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

Pero me gustaría saber si hay un conjunto oficial de funciones para hacer este tipo de cosas y otras operaciones, como el curry en R., en gran medida esto es reducir la cantidad de soportes, palabras clave de funciones, etc. en mi código.

Mi función de 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"

Esto es especialmente bueno para EG agregado:

> 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

Que encuentro mucho más elegante y editable que:

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

Básicamente quiero saber, ¿ya se ha hecho esto por R?

¿Fue útil?

Solución

El lugar estándar para la programación funcional en R es ahora el functional biblioteca.

De la biblioteca:

funcional: curry, componer y otras funciones de orden superior

Ejemplo:

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

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

PD: Esta biblioteca sustituye la ROxigen biblioteca.

Otros consejos

Ambas funciones realmente existen en la roxygen paquete (Vea el código fuente aquí) de Peter Danenberg (originalmente se basó en Solución de Byron Ellis en 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,
                       ...)
}

Tenga en cuenta el uso del Reduce función, que puede ser muy útil cuando se intenta realizar programación funcional en R. ver? Reducir para obtener más detalles (que también cubre otras funciones como Map y Filter).

Y su ejemplo de curry (ligeramente diferente en este uso):

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

Aquí hay un ejemplo para mostrar la utilidad de Compose (aplicando tres funciones diferentes a las letras):

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

Y tu último ejemplo funcionaría así:

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

Por último, aquí hay una manera de hacer lo mismo con plyr (también se podría hacer fácilmente con by o aggregate Como ya se muestra):

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

Hay una función llamada curry en el roxygen paquete.
Encontrado a través de esta conversacion en el archivo de correo R.

Se requiere un enfoque más complejo si desea que los "nombres" de las variables pasen con precisión.

Por ejemplo, si lo haces plot(rnorm(1000),rnorm(1000)) Luego obtendrá buenas etiquetas en sus ejes x e y. Otro ejemplo de esto es 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

No es que el Data.Frame haya asignado nombres útiles a las columnas.

Algunas implementaciones de Curry pueden no hacer esto correctamente, lo que lleva a nombres de columnas ilegibles y etiquetas de trazado. En cambio, ahora uso algo como esto:

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())
    }
}

Esto es bastante complejo, pero creo que es correcto. match.call atrapará a todos los args, recordando completamente qué expresiones definieron los args (esto es necesario para etiquetas agradables). El problema es que atrapa demasiados args, no solo el ... pero también el FUN. También recuerda el nombre de la función que se llama (Curry).

Por lo tanto, queremos eliminar estas dos primeras entradas en .orig de modo que .orig realmente se corresponde con el ... argumentos. Por eso lo hacemos .orig[[1]]<-NULL Dos veces: cada vez elimina una entrada y cambia todo lo demás hacia la izquierda.

Esto completa la definición y ahora podemos hacer lo siguiente para obtener exactamente lo mismo de arriba

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

Una nota final en envir=parent.frame(). Usé esto para asegurarme de que no habrá un problema si tiene variables externas llamadas '.inner' o '.orig'. Ahora, todas las variables se evalúan en el lugar donde se llama al curry.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top