higher level functions in R - is there an official compose operator or curry function?

后端 未结 4 1187
春和景丽
春和景丽 2020-12-07 11:05

I can create a compose operator in R:

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

To be used like this:

 > numericNull         


        
相关标签:
4条回答
  • 2020-12-07 11:46

    A more complex approach is required if you want the 'names' of the variables to pass through accurately.

    For example, if you do plot(rnorm(1000),rnorm(1000)) then you will get nice labels on your x- and y- axes. Another example of this is 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
    

    Not that the data.frame has assigned useful names to the columns.

    Some implementations of Curry may not do this properly, leading to unreadable column names and plot labels. Instead, I now use something like this:

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

    This is quite complex, but I think it's correct. match.call will catch all args, fully remembering what expressions defined the args (this is necessary for nice labels). The problem is that it catches too many args -- not just the ... but also the FUN. It also remembers the name of the function that's being called (Curry).

    Therefore, we want to delete these first two entries in .orig so that .orig really just corresponds to the ... arguments. That's why we do .orig[[1]]<-NULL twice - each time deletes an entry and shifts everything else to the left.

    This completes the definition and we can now do the following to get exactly the same as above

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

    A final note on envir=parent.frame(). I used this to ensure that there won't be a problem if you have external variables called '.inner' or '.orig'. Now, all variables are evaluated in the place where the curry is called.

    0 讨论(0)
  • 2020-12-07 11:54

    Both of these functions actually exist in the roxygen package (see the source code here) from Peter Danenberg (was originally based on 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,
                           ...)
    }
    

    Note the usage of the Reduce function, which can be very helpful when trying to do functional programming in R. See ?Reduce for more details (which also covers other functions such as Map and Filter).

    And your example of Curry (slightly different in this usage):

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

    Here's an example to show the utility of Compose (applying three different functions to letters):

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

    And your final example would work like this:

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

    Lastly, here's a way to do the same thing with plyr (could also easily be done with by or aggregate as already shown):

    > library(plyr)
    > ddply(df, .(l), function(df) paste(toupper(df[,"t"]), collapse=""))
      l   V1
    1 1  ABG
    2 2 DEFH
    3 3  CIJ
    
    0 讨论(0)
  • 2020-12-07 11:58

    The standard place for functional programming in R is now the functional library.

    From the library:

    functional: Curry, Compose, and other higher-order functions

    Example:

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

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

    PS: This library substitutes the ROxigen library.

    0 讨论(0)
  • 2020-12-07 12:09

    There is a function called Curry in the roxygen package.
    Found via this conversation on the R Mail Archive.

    0 讨论(0)
提交回复
热议问题